BZOJ1226 SDOI2009学校食堂(状压dp)
由于Bi<=7,考虑状压。
如果考虑前i个位置的话,状态里需要压入前7个人后7个人,显然是跑不动的。
那么改成考虑前i个人。于是设f[i][j][k]表示前i个人都已吃完饭,i+1后面7个人的吃饭状态为j,最后一个吃饭的人是k的答案。转移时考虑下一个吃饭的是谁即可。
a|b-a&b=a^b。当然没什么用。
各种情况需要考虑的非常清楚。写的跟我一样丑的话就比较难搞了。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 1010 int T,n,a[N],b[N],f[N][1<<7][16],lg2[1<<7|1],l[7]; int main() { #ifndef ONLINE_JUDGE freopen("bzoj1226.in","r",stdin); freopen("bzoj1226.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif T=read(); for (int i=0;i<=7;i++) lg2[1<<i]=i; while (T--) { int n=read(); for (int i=1;i<=n;i++) a[i]=read(),b[i]=read(); memset(f,42,sizeof(f)); f[0][0][7]=0; for (int i=0;i<n;i++) for (int j=0;j<(1<<min(7,n-i-1));j++) { l[0]=min(i+1+b[i+1],n); for (int k=1;k<7;k++) if (!(j&(1<<k-1))) l[k]=min(l[k-1],i+k+1+b[i+k+1]); else l[k]=l[k-1]; for (int k=0;k<16;k++) if (k<=7&&i+k-7>=0||k>=9&&(j&(1<<k-9))) { f[i+1+lg2[j+1&-(j+1)]][j>>lg2[j+1&-(j+1)]+1][7-lg2[j+1&-(j+1)]]= min(f[i+1+lg2[j+1&-(j+1)]][j>>lg2[j+1&-(j+1)]+1][7-lg2[j+1&-(j+1)]],f[i][j][k]+(i+k-7?(a[i+k-7]^a[i+1]):0)); for (int x=1;x<=7;x++) if (!(j&(1<<x-1))&&i+x+1<=l[x-1]) f[i][j|(1<<x-1)][8+x]=min(f[i][j|(1<<x-1)][8+x],f[i][j][k]+(i+k-7?(a[i+k-7]^a[i+x+1]):0)); } } for (int i=1;i<8;i++) f[n][0][0]=min(f[n][0][0],f[n][0][i]); cout<<f[n][0][0]<<endl; } return 0; }