[SDOI2009]學校食堂
阿新 • • 發佈:2020-11-26
題目
做法
狀壓DP。
\(f[i][j][k]\)表示第\(i\)個數字,\(j\)表示最後一個數字在\(j+k\)的位置,然後\(k\)用二進位制表示後面的情況,對於\(f[i][j][k]\),設\(x\)為包括他以內往後\(b[i]+1\)位第一個沒有做菜的位置,然後用\(f[i][j][k]\)貢獻給\(f[x]\)即可。
當然,需要提前處理每個位置的哪些二進位制合法。
#include<cstdio> #include<cstring> #define N 1100 using namespace std; inline int lowbit(int x){return x&-x;} int f[N][10][260]/*表示這個位置自己優先搞到了*/; int n,m,a[N],b[N]; bool he[N][260]/*表示這個屌絲哪個二進位制是非常合法的*/; inline int mymin(int x,int y){return x<y?x:y;} inline int mymax(int x,int y){return x>y?x:y;} void dfs(int x,int k,int limit/*表示限制後面幾位*/,int id)//用來處理合法情況 + { he[id][k]=1; if(x>limit)return ; dfs(x+1,k,mymin(limit,b[id+x]+x),id); dfs(x+1,k^(1<<x),limit,id); } inline int find2(int x){return x==1?0:(x==2?1:(x==4?2:(x==8?3:(x==16?4:(x==32?5:(x==64?6:(x==128?7:8)))))));} inline int cost(int x,int y){return (x|y)-(x&y);} int ans=0; inline void solve(int x,int k,int pre/*前面那個陣列*/,int co)//貢獻情況 { if(x==n+1) { ans=mymin(ans,co); return ; } int shit=(1<<(b[x]+1))-1; for(int ed=shit^k;ed;ed=ed^lowbit(ed)) { int y=find2(lowbit(ed)); if(!he[x][k^(1<<y)])continue; if(f[x][y][k^(1<<y)]==-1 || co+cost(a[x+y],pre)<f[x][y][k^(1<<y)])f[x][y][k^(1<<y)]=co+cost(a[x+y],pre); } } int main() { // freopen("std.in","r",stdin); // freopen("vio.out","w",stdout); int T;scanf("%d",&T); while(T--) { memset(f,-1,sizeof(f)); memset(he,0,sizeof(he)); scanf("%d",&n);ans=999999999; for(int i=1;i<=n;i++) { scanf("%d%d",&a[i],&b[i]); b[i]=mymin(b[i],n-i); } for(int i=1;i<=n;i++)dfs(0,0,7,i); for(int i=0;i<=b[1];i++) { if(he[1][1<<i])f[1][i][1<<i]=0; } for(int i=1;i<=n;i++) { int ed=(1<<(b[i]+1))-1; for(int k=1;k<=ed;k++) { if(!he[i][k])continue; int cnm=find2(lowbit(((1<<(b[i]+2))-1)^k)); for(int t=k;t;t=t^lowbit(t)) { int j=find2(lowbit(t)); if(f[i][j][k]!=-1)solve(cnm+i,k>>cnm,a[i+j],f[i][j][k]); } } } printf("%d\n",ans); } return 0; }