HLG 1473 教主的遗产【状态压缩DP】
题意:
在比赛中会给出n个题,不同做题顺序会有不同的AC系数,假如A先做,B后做的话,B对A的AC系数为4, 反过来 B先做,A后做的话,A对B的AC系数为5,说明先做B后做A将得到更高的AC系数。
设Sij表示第i题在第j题做完后才做获得的AC系数,有三道题a, b, c,做题顺序为bac,则系数和为:Sum = Sab + Scb + Sca。
求一个做题顺序,使得AC系数和最大。
分析: 开始当成是 环状的状态DP ,WA了几个点T T ;
用dp[stat][k] 表示在 状态stat 下以k题结尾的最大分数,k包含在状态stat中
f[stat][k] 表示在 状态stat 下,stat中不包含k题,k与stat中的题目系数和
状态转移方程 dp[stat][k]=f[s][k]+max(dp[s][j]) j 为 状态S中包含的题目
PS: stat 表示状态 比如 n 为 3 ,当 stat=6 的时候,要把stat 看成是 110 这个二进制状态,可以看出因为 stat&(1<<1)为非0
stat&(1<<2)也为非0 ,所以 stat 这个状态中包含第一题和第二题,而不包含第三题。
#include<stdio.h> #include<string.h> int max(int a,int b) { return a>b?a:b; } int dp[1<<16][17]; int g[17][17]; int f[1<<16][17]; int main() { // freopen("D:ce.txt","r",stdin); int st,stat,i,j,k,x,y,n; int z1,z2; while(scanf("%d",&n)!=EOF) { st=(1<<n)-1; for(i=0;i<n;i++) for(j=0;j<n;j++) scanf("%d",&g[i][j]); memset(dp,0xff,sizeof(dp)); memset(f,0,sizeof(f)); for(i=0;i<n;i++) dp[1<<i][i]=0; int stat; int tt,t2; int res=0; for(i=0;i<st;i++) // f[i][k] 表示在 i 这个状态后面 增加一个 k 题后 得到的分数,其中 i 中不能包含 k 这个状态 for(k=0;k<n;k++) // 例如当 n 为3,i 为 2 的时候,i 看成二进制 010 ,这时 k 可以是 0 和2(也就是第 一题和第三题), { if((1<<k)&i) // 所以 f[2(010)][1]=g[0][1]+g[2][1] continue; for(j=0;j<n;j++) if(i&(1<<j)) f[i][k]+=g[k][j]; } for(i=0;i<st;i++) for(j=0;j<n;j++) { if(dp[i][j]<0) // i 这个状态 里要包含 j 这题 continue; tt=0; for(z1=0;z1<n;z1++) if((1<<z1)&i) tt=max(tt,dp[i][z1]); // 找出 i 这个状态中 的最高分数 for(k=0;k<n;k++) { if(i&(1<<k)) // i 这个状态了 不能包含 k 这个题 continue; stat=i+(1<<k); dp[stat][k]=tt+f[i][k]; // dp[stat][k]=f[s][k]+max(dp[s][j]) // f[s][k] 表示 s 的状态下,后面加 k 这题时增加的分数 } // j 属于 0 到 n-1,max(dp[s][j])目的是找出 s 状态中最高 } // 的分数 for(i=0;i<n;i++) res=max(res,dp[st][i]); printf("%d\n",res); } return 0; }