【洛谷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即可