4020. 【雅礼联考DAY02】Revolution (Standard IO)

Description

地图是个矩形的网格。
可以花费一定金钱在一些格子投资。
被投资的格子或者四连通的格子都被投资的话,我就可以获得该格子的收益。
利益最大化是作为商人的基本准则,但这是计算机的任务,拜托您了。

Input

第一行两个数 n,m(n,m ≤ 20),表示矩形的长和宽。
接下来 n 行,每行是 m 个字符组成的字符串,描述投资的花费。
接下来 n 行,每行是 m 个字符组成的字符串,表示该格子的收益。
花费和收益按照一种奇葩的方式给出:
字符 数
‘0’ -’ 9’ 0-9
‘a’ -’ z’ 10-35
‘A’ -’ Z’ 36-61

Output

一个数,表示收益的和减去投资的和的最大值。

Sample Input

【样例 1】
2 2
21
12
21
12
【样例 2】
2 2
ZZ
ZZ
11
11
【样例 3】
3 3
XXX
XXX
XXX
aaa
aZa
aaa
【样例 4】
2 4
asam
atik
123A
45BC
【样例 5】
9 8
IIIIIIII
IIWWWWII
IIWIIIII
IIWIIIII
IIWWWWII
IIIIIWII
IIIIIWII
IIWWWWII
IIIIIIII
IIIIIIII
II0000II
II0II0II
II0II0II
II0000II
II0II0II
II0II0II
II0000II
IIIIIIII

Sample Output

【样例 1】4
【样例 2】0
【样例 3】2
【样例 4】71
【样例 5】606

Data Constraint

n,m ≤ 20.

Solution

学了这么久网络流,还是做不出模板题。
其实网络流的模板并不难,难的只是各种题目的建图。
这一题很明显是黑白染色,我们可以把原来的土地按这样分类
在这里插入图片描述
黑色块分一类,白色块分一类,判断一个块是白色还是黑色只要判断该块行数加上列数的奇偶性即可。
黑色块影响白色块,白色块影响黑色块。
建立超级源点 s s s和超级汇点 t t t
接下来把每个块拆成三个点,我们把这三个点记为 i 1 , i 2 , i 3 i_1,i_2,i_3 i1,i2,i3,该块费用记为 c o s t cost cost,该块收益记为 v a l val val
若该块为白块,则 s s s i 1 i_1 i1连上流量为 i n f inf inf的边, i 1 i_1 i1 i 2 i_2 i2连上流量为 c o s t cost cost的边, i 2 i_2 i2 i 3 i_3 i3连上流量为 v a l val val的边
若该块为黑块,则 i 1 i_1 i1 i 2 i_2 i2连上流量为 v a l val val的边, i 2 i_2 i2 i 3 i_3 i3连上流量为 c o s t cost cost的边, i 3 i_3 i3 t t t连上流量为 i n f inf inf的边
然后我们还要考虑限制条件,若一个块为白色块,记它周围的黑色块标号为 j j j
i 2 i_2 i2 j 1 j_1 j1连向一条流量为 i n f inf inf的边, i 3 i_3 i3 j 2 j_2 j2连向一条流量为 i n f inf inf的边。
最后跑网络流,把总收益减去跑出的数即为答案。

接下来会说明为什么这样是对的,我们首先把每个块的贡献转化。
如果我们选择这个块,那么这个块的贡献即为该块费用
如果我们没选择这个块,那么这个块的贡献即为该块收益
如果我们没选择一个块且它周边的块都被选择了,那么这个块那么这个块的贡献即为0
然后我们按上述说法,选择投资的块,使贡献总和最小,然后用总收益减去最小贡献和,最后求出的值是否就是与题目所求一样?显然是一样的。
那我们跑网络流,跑出来的其实就是最小贡献和。
根据最大流最小割定理,最大流与最小割相等。
考虑两种情况
第一种,黑块周围的白块都被选择
在这里插入图片描述
其中,前面的点除了源点 s s s,其他都为表示白块的点,后面除了汇点 t t t,其他都为表示黑块的点
因为白块都已经选择,表示白块 c o s t cost cost的边一定已经被割去,算进了贡献和里,那么此时 s s s t t t已经不连通,为了满足割最小,那么表示黑块的边就不用再割。
第二种,白块周围的黑块都被选择
其实与第一种情况类似
在这里插入图片描述
黑块表示 c o s t cost cost的边被删去, s s s t t t已经不连通。

所以这样建图符合题目限制,是正确的。

#include<iostream>
#include<cstdio>
#include<queue>
#define N 210
#define M 210000
using namespace std;
int st[N*N+1],tot=1;
struct edge
{
	int to,last,val;
}e[M+1];
void add(int a,int b,int c)
{
	e[++tot].to=b;
	e[tot].val=c;
	e[tot].last=st[a];
	st[a]=tot;
}
void Add(int a,int b,int c){add(a,b,c),add(b,a,0);}
int deep[N*N+1],cur[N*N+1];
bool bfs(int s,int t)
{
	queue<int> q;
	for(int i=s;i<=t;i++)
		deep[i]=0,cur[i]=st[i];
	deep[s]=1;
	q.push(s);
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=st[u];i!=0;i=e[i].last)
		{
			int v=e[i].to;
			if(deep[v]==0&&e[i].val)
			{
				deep[v]=deep[u]+1;
				q.push(v);
			}
		}
	}
	if(!deep[t])
		return false;
	return true;
}
int dfs(int u,int t,int dist)
{
	if(u==t)
		return dist;
	for(int i=cur[u];i!=0;i=e[i].last)
	{
		cur[u]=i;
		int v=e[i].to;
		if(deep[v]==deep[u]+1&&e[i].val)
		{
			int flow=dfs(v,t,min(dist,e[i].val));
			if(flow)
			{
				e[i].val-=flow;
				e[i^1].val+=flow;
				return flow;
			}
		}
	}
	return 0;
}
int dinic(int s,int t)
{
	int res=0;
	while(bfs(s,t))
	{
		int inc;
		while(1)
		{
			inc=dfs(s,t,2147483647);
			if(inc==0)
				break;
			res+=inc;
		}
	}
	return res;
}
const int inf=2147483647;
int n,m;
char s[N+1];
int a[N+1][N+1],b[N+1][N+1];
int count(char ch)
{
	if(ch>='0'&&ch<='9')
		return (int)ch-'0';
	if(ch>='a'&&ch<='z')
		return ch-'a'+10;
	if(ch>='A'&&ch<='Z')
		return ch-'A'+36;
}
int id(int x,int y)
{
	return (x-1)*m+y;
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		for(int j=1;j<=m;j++)
			a[i][j]=count(s[j]);
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		for(int j=1;j<=m;j++)
			b[i][j]=count(s[j]),ans+=b[i][j];
	}
	int s=0,t=id(n,m)*3+3;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			if((i+j)%2==0)
			{
				int x=id(i,j);
				Add(s,3*x,inf);
				Add(3*x,3*x+1,a[i][j]);
				Add(3*x+1,3*x+2,b[i][j]);
				if(i!=1)
				{
					int y=id(i-1,j);
					Add(3*x+1,3*y,inf);
					Add(3*x+2,3*y+1,inf);
				}
				if(i!=n)
				{
					int y=id(i+1,j);
					Add(3*x+1,3*y,inf);
					Add(3*x+2,3*y+1,inf);
				}
				if(j!=1)
				{
					int y=id(i,j-1);
					Add(3*x+1,3*y,inf);
					Add(3*x+2,3*y+1,inf);
				}
				if(j!=m)
				{
					int y=id(i,j+1);
					Add(3*x+1,3*y,inf);
					Add(3*x+2,3*y+1,inf);
				}
			}
			else
			{
				int x=id(i,j);
				Add(3*x,3*x+1,b[i][j] );
				Add(3*x+1,3*x+2,a[i][j]);
				Add(3*x+2,t,inf);
			}
		}
	printf("%d\n",ans-dinic(s,t));
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值