Luogu2157 [SDOI2009] 学校食堂 - dp -
题目链接:https://www.luogu.com.cn/problem/P2157
题解:
注意每个决策会跟其在前面还没有选过的人有关(bi),会和上一次选的人有关
考虑 \(dp[i][S][k]\) 表示考虑到第 \(i\) 个人,\([i,i+7]\) 是否选了的状态是 \(S\) ,最后一个选的位置是 \(i+k\)
转移:
- \(dp[i][S][k] \rightarrow dp[i+1][S>>1][k-1]\),其中 \(S\& 1=1\),这就是当前人已经取完了的情况,注意此时不参与下面的转移
- \(dp[i][S][k] + f(k,l) \rightarrow dp[i][S|(1<<l)][l]\),其中 \(f(k,l)\) 就是做这个菜的时间,而且有 \(l \notin S\)
细节若干:
- \(k \in [-8,7]\) -8 是因为极端情况下 \(b_i=7\),然后这个人等后面7个人都吃完了再吃,这样转移到的是 \(i+8\)
- S 枚举到 \((2^8-1)\),而不是 \(2^{b_i+1}-1\),这是因为可能出现下面的第三种情况
- 题意里的小细节:注意是当前人才会愤怒,而且比如说当前第 \(i\) 个人的时候让 \(i+2\) 先走,然后该 \(i+1\) 走的时候,此时 \(i+2\) 已经没有了,紧跟着的第一个人从 \(i+3\) 算起,这也就意味着不能预处理,只能根据状态 \(S\) 求出当前人最远能容忍到哪里
// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>
using namespace std;
typedef long long ll;
typedef long long LL;
const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 1005;
int n,a[maxn],b[maxn];
ll dp[maxn][(1 << 8)+5][17];
int st[maxn][9];
void ck(ll &a,ll b){a = min(a, b);}
void solve(){
memset(dp,0x3f,sizeof dp);
memset(st,0,sizeof st);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]),st[i][0]=1;
for(int k=1;k<=7;k++){
for(int i=1;i+k<=n;i++){
if(k <= b[i]){
st[i][k] = 1;
for(int j=i+1,t=k;j<=i+k-1;j++,t--){
st[i][k] &= st[j][t-1];
}
}
}
}
for(int k=0;k<=b[1];k++)if(st[1][k]){
dp[1][1 << k][k + 8] = 0;
}
for(int i=1;i<=n;i++)
for(int S=0;S<=(1 << b[i]+1)-1;S++){
for(int k=max(-8, -i+1);k<=7&&i+k<=n;k++){
if(S & 1){
ck(dp[i+1][S >> 1][k+7], dp[i][S][k+8]);
continue;
}
if(k >=0){
if((S&(1<<k)) && k <= b[i]);
else continue;
}
// k + 8
int fi = inf;
for(int l=0;l<=b[i]&&i+l<=n;l++)if((S&(1<<l)) == 0){
if(i+l>fi)break;
fi = min(fi, i+l+b[i+l]);
int T = S ^ (1<<l);
ck(dp[i][T][l + 8], dp[i][S][k + 8] + (a[i+k]|a[i+l]) - (a[i+k]&a[i+l]));
}
}
}
ll res = inf;
for(int i=-8;i<=0;i++)ck(res, dp[n][1][i + 8]);
cout << res << '\n';
}
signed main(){
// freopen("Luogu2157.in","r",stdin);
int te=1;scanf("%d",&te);
while(te--)solve();
return 0;
}