题目链接:HDU 1074 Doing Homework
状态方程跟ZOJ那个爆炸的题差不多,dp[newS].reduced = min(dp[newS].reduced, dp[s].reduced + max(dp[s].time + hw[i].cost - hw[i]._end, 0); dp用一个结构体保存,加一个变量表示做的前一个作业。这里1表示当前位置的作业还没做,0表示当前位置的作业做了,要由全没做推到全做了,所以外层循环需要从后往前推,题目已经说明给出的作业顺序是字典序的,所以内层循环只要由小到大就可以配合外层循环保证输出也是字典序。
#include <iostream> #include <cstring> #include <cstdio> #include <stack> using namespace std; const int MAX_N = 15; const int MAX_S = (1 << MAX_N) + 100; const int MAX_M = MAX_N + 3; const int MAX_C = 100 + 10; const int INF = 0x3f3f3f3f; struct HomeWork { int _end, cost; char name[MAX_C]; }; HomeWork hw[MAX_M]; struct DP { int pre, time, reduced; }; DP dp[MAX_S]; stack <int> sta; int main() { //freopen("in.txt", "r", stdin); int T, n; scanf("%d", &T); while(T--) { scanf("%d", &n); for(int i = 0; i < n; i++) scanf("%s%d%d", hw[i].name, &hw[i]._end, &hw[i].cost); memset(dp, INF, sizeof(dp)); int S = (1 << n) - 1, newS; dp[S].pre = -1, dp[S].reduced = dp[S].time = 0; for(int s = S; s > 0; s--) { for(int i = 0; i < n; i++) //注意顺序,这样就可以字典序输出了 { if(s & (1 << i)) //第i个作业还没做 { newS = s ^ (1 << i); dp[newS].time = dp[s].time + hw[i].cost; // 赋初值 if(dp[newS].reduced > dp[s].reduced + max(dp[newS].time - hw[i]._end, 0)) { dp[newS].pre = i; dp[newS].reduced = dp[s].reduced + max(dp[newS].time - hw[i]._end, 0); } } } } printf("%d\n", dp[0].reduced); int k = dp[0].pre, state = 0; while(k != -1) { sta.push(k); state = (1 << k) | state; k = dp[state].pre; } while(!sta.empty()) { printf("%s\n", hw[sta.top()].name); sta.pop(); } } return 0; }