P2157 [SDOI2009]学校食堂 状压DP

it2023-10-05  70

题意:

有点复杂,自行浏览吧 题目链接

分析:

我们发现DP转移时需要记录以下几个信息:

打饭队列的队首是谁,上一个打饭的是谁,队列前 b [ i ] b[i] b[i]个人的状态

然后我们根据这些信息设立DP状态,记 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示该第 i i i个人打饭(等价于前 i − 1 i-1 i1个人已经买完饭)此时队列前7个人的状态是 j j j,上一个打饭的人是 i + k i+k i+k。由于打饭的人在 i i i的前后都可以,所以 k k k的范围就是 [ − 8 , 8 ] [-8,8] [8,8],加上偏移量就是 [ 0 , 16 ] [0,16] [0,16]

接下来我们考虑转移,分为两种情况:

i i i个人已经买完饭了,也就是说直接将状态转移到 i + 1 i+1 i+1就可以了 f[i+1][j>>1][k-1]=min(f[i+1][j>>1][k-1],f[i][j][k]); i i i个人还没有买饭,那就在所有人都能忍受的范围内枚举买饭的人是谁 f[i][j|(1<<l)][l]=min(f[i][j|(1<<l)][l],f[i][j][k]+(t[i+k]^t[i+l]));

状态初始化为 f [ 1 ] [ 0 ] [ 7 ] f[1][0][7] f[1][0][7]表示该第 1 1 1个人买饭,此时身后所有人都没有买完,上一个买的人是第 7 − 8 7-8 78个,最后统计枚举 i i i然后取 f [ n + 1 ] [ 0 ] [ i ] f[n+1][0][i] f[n+1][0][i]的最小值

代码:

#include<bits/stdc++.h> using namespace std; namespace zzc { const int maxn = 1005; int f[maxn][256][20],b[maxn],t[maxn]; int T,n; void work() { scanf("%d",&T); while(T--) { memset(f,0x3f,sizeof(f)); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&t[i],&b[i]); f[1][0][7]=0; for(int i=1;i<=n;i++) { for(int j=0;j<(1<<8);j++) { for(int k=-8;k<=7;k++) { if(f[i][j][k+8]!=0x3f3f3f) { if(j&1) f[i+1][j>>1][k+7]=min(f[i+1][j>>1][k+7],f[i][j][k+8]); else { int lim=0x3f3f3f; for(int l=0;l<=7;l++) { if(!((j>>l)&1)) { if(i+l>lim) break; lim=min(lim,i+l+b[i+l]); f[i][j|(1<<l)][l+8]=min(f[i][j|(1<<l)][l+8],f[i][j][k+8]+(i+k?t[i+k]^t[i+l]:0)); } } } } } } } int ans=0x3f3f3f; for(int i=0;i<=8;i++) ans=min(ans,f[n+1][0][i]); printf("%d\n",ans); } } } int main() { zzc::work(); return 0; }
最新回复(0)