洛谷 P1280 尼克的任务(DP,记忆化搜索)

链接: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

因为要求最大空暇时间,那么就是求最短工作时间,那么问题就变成了求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
#include
#include
#include
#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;
}

你可能感兴趣的:(★动态规划,★水题之路,#,【基础DP】)