题意:
有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;
}