课程大作业 POJ15288状态压缩dp

题目

D:课程大作业

总时间限制: 1000ms 内存限制: 65536kB
描述
小明是北京大学信息科学技术学院三年级本科生。他喜欢参加各式各样的校园社团。这个学期就要结束了,每个课程大作业的截止时间也快到了,可是小明还没有开始做。每一门课程都有一个课程大作业,每个课程大作业都有截止时间。如果提交时间超过截止时间X天,那么他将会被扣掉X分。对于每个大作业,小明要花费一天或者若干天来完成。他不能同时做多个大作业,只有他完成了当前的项目,才可以开始一个新的项目。小明希望你可以帮助他规划出一个最好的办法(完成大作业的顺序)来减少扣分。

输入
输入包含若干测试样例。
输入的第一行是一个正整数T,代表测试样例数目。
对于每组测试样例,第一行为正整数N(1 <= N <= 15)代表课程数目。
接下来N行,每行包含一个字符串S(不多于50个字符)代表课程名称和两个整数D(代表大作业截止时间)和C(完成该大作业需要的时间)。
注意所有的课程在输入中出现的顺序按照字典序排列。
输出
对于每组测试样例,请输出最小的扣分以及相应的课程完成的顺序。
如果最优方案有多个,请输出字典序靠前的方案。
样例输入
2
3
Computer 3 3
English 20 1
Math 3 2
3
Computer 3 3
English 6 3
Math 6 3
样例输出
2
Computer
Math
English
3
Computer
English
Math

解决思路

前两天做到这个题,花了挺长时间解决,分享一下自己的思路吧。我一开始想到的办法是建一个Ctask类来存储每个任务的名字,截止日期和完成日期,然后以总扣分是否下降为标准来进行一个冒泡排序。
这个做法是O(n^3)的复杂度,并不超时,对于给出的简单样例和自己编的数据都是对的。但是交上去是WA,后来我发现这种做法的思路有问题。这种做法是相当于一个贪心的思路,不断地取局部最优,但是题中的排列却并不具有局部最优推到全局最优的性质,所以只能是列举所有,进行dp;
在这个过程中我们发现可以通过位上的1或0来表示任务是否做了,由此可以方便地举出所有类型的例子,通过状态转移方程来确保每种局部任务的做法都是最优化的选择。从而最终得到结果。
我是参考的这个博客给出的代码,链接: link.
这个博客给出的代码很简洁,而且在存储做法顺序的时候采取了一种很巧妙的办法,我在这个基础上写了自己的代码。个人理解所谓状态压缩就是用位来表示状态。

代码

#include
#include
#include
using namespace std;
const int Max (1 << 16);
int D[16];
int C[16];
int sum[Max];//当前做法下完成任务总用时
string name[16];
string ans[Max];//用来存储做法
int dp[Max];//当前做法下的最少扣分
int f(int i, int j) {
	return max(0, i - j);
}
int main() {
	int T;
	cin >> T;
	while (T--)
	{
		int N; 
		cin >> N;
		for (int i = 0; i < N; i++)
		{
			cin >> name[i];
			cin >> D[i];
			cin >> C[i];
			sum[1 << i] = C[i];
		}
		for (int i = 1; i < 1<<N; i++)
		{
			for (int j = 0; j < N; j++)
			{
				if (i & (1 << j)) {
					sum[i] = sum[i ^ (1 << j)] + C[j];
					}
			}
		}//得到所有用时,下面开始更新扣分
		for (int i = 0; i < 1<<N; i++)
		{
			ans[i] = "";
		}//初始化答案串
		dp[0] = 0;
		for (int i = 1; i <1<<N ; i++)
		{
			for (int j = 0; j < N; j++)
			{
				if (i & (1 << j) && (dp[i ^ (1 << j)] + f(sum[i], D[j]) <= dp[i] || ans[i] == "")) {
					if (dp[i ^ (1 << j)] + f(sum[i], D[j]) < dp[i] || ans[i ^ (1 << j)] + name[j] + "\n" < ans[i] || ans[i] == "") {
						ans[i] = ans[i ^ (1 << j)] + name[j] + "\n";
					}
					dp[i] = dp[i ^ (1 << j)] + f(sum[i], D[j]);
				}
			}
		}
		cout << dp[(1 << N )- 1] << endl;
		cout << ans[(1 << N) - 1] ;
	}
	return 0;
}

你可能感兴趣的:(POJ,算法,动态规划,数据结构,c++)