本文已参与「新人创作礼」活动,一起开启掘金创作之路。
活动安排问题
描述
Jack是一名nwpu的大一新生,对学校举办的各种活动都十分的好奇,想尽可能多的参加这些活动。Npwu每天共有N项活动,其开始结束时间分别为B[i],E[i],(i = 1,2,……N) 请问Jack一天最多能参加几项活动。当然,Jack在同一时间内只能参加一项活动,即jack参加的活动时间上不能重叠,但时间为[t1,t2],[t2,t3]的两个活动是可以同时参加的。
输入
第一行 一个整数N(1<=n<=1000)表示活动总数。 接下来N行表示各活动的起始,结束时间0<=B[i]<E[i]<24
输出
一个整数表示Jack最多能参加的活动数目。
输入样例
4 10 11 2 3 8 10 0 2
输出样例
4
提示 printf("%d\n,num");
思路
方法一:
题意很明显希望参加的活动数目尽量多。对于活动安排问题可以采取动态规划的策略:
首先将活动的结束时间按照第一关键字排序(由小到大),再将活动的开始时间作为第二关键字排序(由小到大)
定义dp[i]表示在前i场比赛中最多可以参加几场比赛,
由此得出方程:dp[i]=max(dp[i-1],dp[temp]+1);
temp指从dp[i-1]向前找到的第一个允许参加第i场活动的活动编号,由它推导出dp[i]=dp[temp]+1;
由于每次循环时都向前找一次temp会浪费太多时间,又因为活动开始或结束时间是单调递增的,
故可以令temp在循环时逐步递增,这样时间复杂度就降到了O(n).
这里只是讲解一下动态规划的想法。就不写代码了,动态规划的代码和下面的贪心方法相似。只是这种动态规划的思路是基于贪心的思想来实现的。
方法二:
这个问题可以抽象为在一个数轴上有n条线段,现要选取其中k条线段使得这k条线段两两没有重合部分,问最大的k为多少。 最左边的线段放什么最好? 显然放右端点最靠左的线段最好,从左向右放,右端点越小妨碍越少。 其他线段放置按右端点排序,贪心放置线段,即能放就放。
以上两种方法的时间复杂度都是O(nlogn),快速排序的时间复杂度是O(nlogn),而动态规划或者贪心执行更新策略的时间复杂度是O(n).
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=2e3+50;
int n;
struct Node{
int s,f;
}a[maxn];
bool cmp(Node x,Node y){
if(x.f==y.f)return x.s<y.s;
return x.f<y.f;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i].s>>a[i].f;
sort(a+1,a+1+n,cmp);
int ans=1,now=1,opt=2;
while(opt<=n){
if(a[opt].s>=a[now].f){
ans++;
now=opt;
}
opt++;
}
cout<<ans<<endl;
return 0;
}