题意:
有点复杂,自行浏览吧 题目链接
分析:
我们发现DP转移时需要记录以下几个信息:
打饭队列的队首是谁,上一个打饭的是谁,队列前(b[i])个人的状态
然后我们根据这些信息设立DP状态,记(f[i][j][k])表示该第(i)个人打饭(等价于前(i-1)个人已经买完饭)此时队列前7个人的状态是(j),上一个打饭的人是(i+k)。由于打饭的人在(i)的前后都可以,所以(k)的范围就是([-8,8]),加上偏移量就是([0,16])
接下来我们考虑转移,分为两种情况:
- 第(i)个人已经买完饭了,也就是说直接将状态转移到(i+1)就可以了
f[i+1][j>>1][k-1]=min(f[i+1][j>>1][k-1],f[i][j][k]);
- 第(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])表示该第(1)个人买饭,此时身后所有人都没有买完,上一个买的人是第(7-8)个,最后统计枚举(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
",ans);
}
}
}
int main()
{
zzc::work();
return 0;
}