一、题目
( t zxy) 有 (n) 个宝箱,第 (i) 个宝箱有 (a_i) 个硬币,商店出售 (m) 个钥匙,第 (i) 个钥匙需要 (b_i) 个硬币。
( t ppl) 想要趁 ( t zxy) 不在的时候打开他的宝箱拿走硬币,他的收益定义为开宝箱得到的硬币减去买钥匙消耗的硬币,如果收益为正那么 ( t ppl) 就赚了!
可惜 ( t zxy) 会在 ( t ppl) 来之前给宝箱上锁,第 (i) 把钥匙可以打开第 (i) 把锁,给宝箱 (i) 上锁 (j) 需要花费 (c[i][j]),( t zxy) 想知道不让 ( t ppl) 赚钱那么最少需要多少花费,如果 ( t ppl) 稳赚那么输出 (-1)
(1leq n,mleq 6,1leq a_i,b_ileq 4)
二、解法
首先考虑 ( t ppl) 的操作,不难发现这就是一个最大权闭合子图,可以构建出网络流模型:原点连宝箱容量为 (a_i),钥匙连汇点容量为 (b_i),钥匙连它锁了的宝箱,那么赚的钱是 (sum a_i-maxflow),如果 (maxflow=sum a_i) 就不能赚钱。
( t 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
",ans);
}