链接:P1280
题目描述
尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成。
尼克的一个工作日为N分钟,从第一分钟开始到第N分钟结束。当尼克到达单位后他就开始干活。如果在同一时刻有多个任务需要完成,尼克可以任选其中的一个来做,而其余的则由他的同事完成,反之如果只有一个任务,则该任务必需由尼克去完成,假如某些任务开始时刻尼克正在工作,则这些任务也由尼克的同事完成。如果某任务于第P分钟开始,持续时间为T分钟,则该任务将在第P+T-1分钟结束。
写一个程序计算尼克应该如何选取任务,才能获得最大的空暇时间。
输入格式:
输入数据第一行含两个用空格隔开的整数N和K(1≤N≤10000,1≤K≤10000),N表示尼克的工作时间,单位为分钟,K表示任务总数。
接下来共有K行,每一行有两个用空格隔开的整数P和T,表示该任务从第P分钟开始,持续时间为T分钟,其中1≤P≤N,1≤P+T-1≤N。
输出格式:
输出文件仅一行,包含一个整数,表示尼克可能获得的最大空暇时间。
输入样例#1:
15 6
1 2
1 6
4 11
8 5
8 1
11 5
输出样例#1:
4
分析
感觉这题直接DP比较难想,于是想用递归+记忆化的方法。
用 vector a[maxn] 来存储任务。
a[P] 中存放 从第P分钟的开始 的 所有任务 的 结束时间的下一分钟(即P+T)
然后转化为一个DAG模型来看,i 能到达的点 就是 a[i][j] ( 0<=j<a[i].size() )。
因为要求最大空暇时间,那么就是求最短工作时间,那么问题就变成了求DAG最短路问题。
但要注意的是,当a[i]为空,并不是没有出路,而要 i++ 直至a[i]不为空(即有任务要做)。因为时间不会停止,还是得等着做下一个任务,当i > N才是末尾,没有其他出路。
设dp[i]:表示从第 i 分钟开始(直至第N分钟)最短工作时间
得状态转移方程:dp[i] = min{ dp[ a[i][j] ]+a[i][j]-i } ( 0 <= j < a[i].size() )
即在第 i 分钟开始的任务中选择dp+T最小的那个
以下代码:
#include<cstdio>
#include<climits>
#include<vector>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=10010;
int N,K,dp[maxn];
vector<int> a[maxn];
int DP(int i)
{
if(dp[i]!=INT_MAX) //记忆化
return dp[i];
int s=i;
while(a[i].empty()&&i<=N) //找到下一个任务开始的时间
i++;
if(i>N)
return 0;
for(int j=0;j<a[i].size();j++)
{
int temp=DP(a[i][j])+a[i][j]-i;
if(temp<dp[i]) //找到最小值
dp[i]=temp;
}
fill(dp+s,dp+i,dp[i]); //s~i-1为空暇时间,故均等于dp[i]
return dp[i];
}
int main()
{
fill(dp,dp+maxn,INT_MAX); //初始化dp为一个足够大的值
scanf("%d %d",&N,&K);
for(int i=1;i<=K;i++)
{
int P,T;
scanf("%d %d",&P,&T);
a[P].push_back(P+T);
}
printf("%d",N-DP(1));
return 0;
}