Doing Homework HDU - 1074(基础状压DP)

题目传送门

题意:

有n门课程,对于每门课都有对应的交作业截止时间和完成作业所需时间,每超过期限一天就扣一分,求最少扣分数以及路径(扣分相同的情况下,按字典序输出路径)。

思路:

可以用二进制数表示当前状态,1表示做了,0表示没做,就比如有4门课,那么1100表示1、2写了,3、4没写。然后嘛…直接暴力枚举每一种情况,体现在循环中就是从0到(1<<n)-1枚举出每一个数,就拿上面的例子来说:1100这个状态,肯定是由1000,0100这两个状态推出来的,讲到这里,写出动态转移方程问题应该问题不大,主要是需要对位运算有一定的了解。这里放一篇关于位运算的博客,挺详细的:传送门
至于另一个要求(按字典序输出路径)嘛,通过遍历更新当前dp[i]的时候,倒着来就行了。输出路径的话跑一遍dfs就行了。

Code:

struct node {
    
    
	string name;
	int cost, dead;
}s[20];
struct node2 {
    
    
	int pre, pos, soc, tim;
}dp[1<<15]; 
void dfs(int p) {
    
     //路径输出
	if (p == 0) {
    
    
		return ;
	}
	dfs(dp[p].pre);
	cout << s[dp[p].pos].name << endl;
}
int main() {
    
    
	int t;
	cin >> t;
	while (t--) {
    
    
		int n;
		cin >> n;
		mem(dp, 0); 
		for (int i = 0; i < n; i++) cin >> s[i].name >> s[i].dead >> s[i].cost;
		int en = 1 << n;
		for (int i = 1; i < en; i++) {
    
    
			dp[i].soc = INF;
			for (int j = n-1; j >= 0; j--) {
    
     //按字典序来
				int tmp = 1 << j;
				if (tmp & i) {
    
     // 看当前状态i包不包含状态tmp
					int pos = i - tmp;
					int tim = dp[pos].tim + s[j].cost - s[j].dead;
					if (tim < 0) tim = 0;
					if (tim + dp[pos].soc < dp[i].soc) {
    
    
						dp[i].soc = tim + dp[pos].soc;
						dp[i].pre = pos;
						dp[i].pos = j;
						dp[i].tim = dp[pos].tim + s[j].cost;
					}
				}
			}
		}
		cout<<dp[en-1].soc<<endl; 
        dfs(en-1);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/liudashuai666/article/details/113404320