【BZOJ】【P1226】【SDOI2009】【学校食堂Dining】【状压DP】【题解】

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

 

你可能感兴趣的:(dp,bzoj)