CF1519F Chests and Keys
一、题目
\(\tt zxy\) 有 \(n\) 个宝箱,第 \(i\) 个宝箱有 \(a_i\) 个硬币,商店出售 \(m\) 个钥匙,第 \(i\) 个钥匙需要 \(b_i\) 个硬币。
\(\tt ppl\) 想要趁 \(\tt zxy\) 不在的时候打开他的宝箱拿走硬币,他的收益定义为开宝箱得到的硬币减去买钥匙消耗的硬币,如果收益为正那么 \(\tt ppl\) 就赚了!
可惜 \(\tt zxy\) 会在 \(\tt ppl\) 来之前给宝箱上锁,第 \(i\) 把钥匙可以打开第 \(i\) 把锁,给宝箱 \(i\) 上锁 \(j\) 需要花费 \(c[i][j]\),\(\tt zxy\) 想知道不让 \(\tt ppl\) 赚钱那么最少需要多少花费,如果 \(\tt ppl\) 稳赚那么输出 \(-1\)
\(1\leq n,m\leq 6,1\leq a_i,b_i\leq 4\)
二、解法
首先考虑 \(\tt ppl\) 的操作,不难发现这就是一个最大权闭合子图,可以构建出网络流模型:原点连宝箱容量为 \(a_i\),钥匙连汇点容量为 \(b_i\),钥匙连它锁了的宝箱,那么赚的钱是 \(\sum a_i-maxflow\),如果 \(maxflow=\sum a_i\) 就不能赚钱。
\(\tt zxy\) 能决定的就只有宝箱和钥匙之间怎么连边,考虑我们是怎么爆搜的,就是考虑每个钥匙的流量,然后对于每个宝箱都枚举给钥匙多少流量。由于数据范围小得离谱可以考虑状压,设 \(dp[s][i][j][k]\) 表示宝箱流量的状态是 \(s\),现在考虑的宝箱是 \(i\),钥匙是 \(j\),钥匙的流量是 \(k\) 的最小花费,转移就照着爆搜写就行了,我算了一下大约 \(O(1e7)\) 就算的出来。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int inf = 0x3f3f3f3f;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,k,ans,a[7],b[7],c[7][7],dp[20000][7][7][5];
struct node {int a[7];node() {memset(a,0,sizeof a);} };
int get(node s)
{
int x=0;
for(int i=1;i<=n;i++) x=x*5+s.a[i];
return x;
}
node back(int x)
{
node s;
for(int i=n;i>=1;i--) s.a[i]=x%5,x/=5;
return s;
}
int check(node s)
{
for(int i=1;i<=n;i++)
if(s.a[i]<a[i]) return 0;
return 1;
}
signed main()
{
n=read();m=read();k=1;
for(int i=1;i<=n;i++)
a[i]=read(),k*=5;
for(int i=1;i<=m;i++)
b[i]=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
c[i][j]=read();
memset(dp,0x3f,sizeof dp);
ans=inf;dp[0][1][1][0]=0;
for(int s=0;s<k;s++)
{
node t=back(s);int f=1;
for(int i=1;i<=n;i++)
if(t.a[i]>a[i]) f=0;
if(!f) continue;//this state is illegal
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++)
for(int r=0;r<5;r++)
{
if(r>b[j]) break;
for(int f=0;f<5;f++)
{
if(r+f>b[j] || t.a[i]+f>a[i]) break;
node tt=t;tt.a[i]+=f;
int ts=get(tt),ti=i+1,tj=j,tr=r+f,ad=f?c[i][j]:0;
if(ti>n) ti=1,tj++,tr=0;
if(check(tt)) ans=min(ans,dp[s][i][j][r]+ad);
if(tj<=m)
dp[ts][ti][tj][tr]=min(dp[ts][ti][tj][tr],dp[s][i][j][r]+ad);
}
}
}
if(ans>=inf) puts("-1");
else printf("%d\n",ans);
}