考察:状压dp
错误思路:
贪心思想,每个作业用ed-cost = st排序,输出路径就是排序后的路径.
错误数据
1
2
a 6 14 b 10 7
很明显可以发现如果数据中出现了ed-cost <0就会错误,按贪心思路是先选a,但是最优解是先选b.很明显先做完能及时做完的可以减少减去的分数.
正确思路:
状压dp,状压dp实际上就是枚举所有可行的方案.但这道题我们需要记录路径,也就是记录由k->i的k状态.最后递归输出.这里可以参考 AcWing 1076的记录路径,将dp数组换成结构体,记录上一状态.
这里n范围很小,属于状压dp的可行范围.
这道题是状压dp,实际是在枚举所有到达终点的方式.与背包dp不同的是,这里不是组合问题,而是排列问题,并且n很小,最后每个都要选.
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <string> 5 using namespace std; 6 const int N = 15; 7 struct HW{ 8 int ed,cost; 9 string s; 10 }hw[N]; 11 struct F{ 12 int cost,st,pre,pos;//花费,开始时间,上一个状态 13 }f[(1<<N)+10]; 14 int n; 15 void inits() 16 { 17 f[0].pos =f[0].pre = -1; 18 for(int i=1;i<1<<n;i++) 19 { 20 f[i].pre = -1; 21 f[i].cost = 0x3f3f3f3f; 22 f[i].st = 0; 23 f[i].pos = -1; 24 } 25 } 26 void dfs(int state) 27 { 28 if(f[state].pre!=-1) 29 { 30 int v = f[state].pre; 31 dfs(v); 32 } 33 if(f[state].pos!=-1) 34 printf("%s\n",hw[f[state].pos].s.c_str()); 35 } 36 int main() 37 { 38 int T; 39 scanf("%d",&T); 40 while(T--) 41 { 42 scanf("%d",&n); 43 for(int i=0;i<n;i++) 44 cin>>hw[i].s>>hw[i].ed>>hw[i].cost; 45 inits(); 46 for(int i=1;i<1<<n;i++) 47 { 48 for(int j=n-1;j>=0;j--) 49 if(i>>j&1) 50 { 51 int k = i-(1<<j); 52 int cost = max(f[k].st+hw[j].cost-hw[j].ed,0); 53 if(f[k].cost+cost<f[i].cost) 54 { 55 f[i].pre = k; 56 f[i].cost = f[k].cost+cost; 57 f[i].st = f[k].st+hw[j].cost; 58 f[i].pos = j; 59 } 60 } 61 } 62 printf("%d\n",f[(1<<n)-1].cost); 63 dfs((1<<n)-1); 64 } 65 return 0; 66 }