传送门: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;
}