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);
}
posted @ 2021-05-03 22:33  C202044zxy  阅读(83)  评论(0编辑  收藏  举报