【洛谷P3947】肝活动

本人是NOIP2018省二的蒟蒻,最近练习状压,总感觉楼下的题解晦涩难懂,我决定自己写一篇题解造福一下广大蒟蒻


首先看数据范围就猜到了状压dp……

设f[i]表示完成状态i(二进制位,状压)这些歌获得的奖励;

则答案为f[(1<<n)-1]。

对于一个状态i,若第j首歌没完成,即i&(1<<j-1)==0,则有

f[i|(1<<j-1)]=max(f[i]+完成j的奖励)

关于无解的情况:最终答案<m 或者 完成所有的时间超过限制

关于输出方案:每一次状态转移时记录当前状态i的前驱j以及从j转移到i所完成的那一首歌

具体实现请见代码

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5 int n,m,t,f[1<<23];
 6 int timee[25],s[25],tot;
 7 char name[25][51];
 8 int tt[1<<23],bef[1<<23],ans[1<<23];
 9 void printans(int now) {
10     if(!now) return ;
11     printans(bef[now]);
12     puts(name[ans[now]]);
13 }
14 int main() {
15     scanf("%d%d%d",&n,&m,&t);
16     for(int i=1;i<=n;i++) {
17         scanf("%s",name[i]);
18         scanf("%d%d",&timee[i],&s[i]);
19         tot+=timee[i];
20     }
21     memset(f,-1,sizeof(f));
22     f[0]=0;
23     for(int i=0;i<1<<n;i++) {
24         for(int j=1;j<=n;j++) {
25             if(i&(1<<j-1)) continue ;
26             int next=i|(1<<j-1);
27             int val=max(s[j]-timee[j]-tt[i],0);
28             if(f[next]<f[i]+val) {
29                 f[next]=f[i]+val;
30                 tt[next]=tt[i]+timee[j];
31                 bef[next]=i;
32                 ans[next]=j;
33             }
34         }
35     }
36     if(tot>t||f[(1<<n)-1]<m) {
37         puts("No Answer");
38         return 0;
39     }
40     printf("%d\n",f[(1<<n)-1]);
41     printans((1<<n)-1);
42     return 0;
43 }

time变量在洛谷上评测好像CE,所以只能用timee了

ps:奖励可能是负数,所以和0取一个max即可

posted @ 2019-03-09 14:50  AD_shl  阅读(144)  评论(0编辑  收藏  举报