传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1226
蒟蒻最近在刷DP题……
由于每个人的忍耐人数<=7所以我们可以把连同自己在内的8个人压缩到一个状态
影响结果的因素还有上个是谁吃的饭于是再加一维
f[i][j][k]表示前i个人,[i,i+7]八个人状态为j,上次吃饭的人与i的相对位置为k,最小的时间
考虑两种情况:
1.i已经吃了,那么可以推f[i+1][i+1到i+8 的状态][k-1]/*因为相对位置改变*/ = f[ i ][ j ][ k ]
2.i没有吃, 那么枚举没吃的人 L
f[i][i到i+7 的状态 使 L吃饭][L] = min(本身,f[i][j][k]+cost(k,l) )
cost(k,l)=(t[k+i]|t[l+i])-(t[k+i]&t[l+i])=t[k+i]^t[l+i](自己想想)//感谢gaotianyu1350神犇的提醒,更正一个错误Orz!!
边界:f[1][(1<<8)-1][-1+8]=0;
在代码实现上k取[0,15]来代表[-8,7]
蒟蒻还傻X的用1表示没吃0表示吃 j的二进制数从右往左如00000010 表示i吃了,i+1没吃……
Code:
/* ID:zky OJ:BZOJ Index:1226 Language:C++ */ #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int T,n,B; int f[1011][1<<8][17]; int t[1011]; int b[1011]; int minn; int main(){ cin>>T; while(T--){ cin>>n; for(int i=1;i<=n;i++) cin>>t[i]>>b[i]; memset(f,0x7f,sizeof(f)); B=f[0][0][0]; f[1][(1<<8)-1][-1+8]=0; for(int i=1;i<=n;i++) for(int j=(1<<8)-1;j>=0;j--) for(int k=-8;k<=7;k++){ if(f[i][j][k+8]==B)continue; if((j&1)==0){f[i+1][(j>>1)|(1<<7)][k-1+8]=f[i][j][k+8];continue;} minn=B; for(int l=0;l<8;l++){ if(!(j&(1<<l)))continue; if(i+l>minn)break; minn=min(minn,i+l+b[i+l]); int deb1=j&(~(1<<l)); if(i+k<=0) f[i][j&(~(1<<l))][l+8]=min(f[i][j&(~(1<<l))][l+8],0); else f[i][j&(~(1<<l))][l+8]=min(f[i][j&(~(1<<l))][l+8], f[i][j][k+8]+(t[i+k]^t[i+l])); } } int ans=B; for(int i=0;i<8;i++) ans=min(ans,f[n+1][(1<<8)-1][i]); cout<<ans<<endl; } return 0; }