由于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_JUDGEfreopen("bzoj1226.in","r",stdin);freopen("bzoj1226.out","w",stdout);const char LL[]="%I64d\n"; #elseconst char LL[]="%lld\n"; #endifT=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; }