【ARC074F】Lotus Leaves(最小割)

遇到这种整列整行的题可以考虑用一个节点表示整一列或者建一个虚拟节点向这一列的每个点连边。

然后最小割的定义:把所有节点分到分别包含 S S S T T T 的两个集合内,求 S S S 所在集合指向 T T T 所在集合的所有边的边权和的最小值。

也就是割成两半的最小代价,这个可以联想到:

  1. 图论,把图分成两半的最小代价。

  2. 很多人做选择,做完选择的最小代价。

这道题就是联想到了第 1 1 1 点。

代码如下:

#include<bits/stdc++.h>

#define N 110
#define INF 0x7fffffff
#define get(x,y) ((x-1)*m+y)

using namespace std;

int n,m,s,t,stx,sty,edx,edy;
int cnt=1,head[N<<1],cur[N<<1],to[N*N*4],nxt[N*N*4],c[N*N*4];
int num[N<<1];
char ch[N][N];

queue<int>q;

void adde(int u,int v,int ci)
{
	to[++cnt]=v;
	c[cnt]=ci;
	nxt[cnt]=head[u];
	head[u]=cnt;
	
	to[++cnt]=u;
	c[cnt]=0;
	nxt[cnt]=head[v];
	head[v]=cnt;
}

bool bfs()
{
	memset(num,-1,sizeof(num));
	memcpy(cur,head,sizeof(cur));
	q.push(s);
	num[s]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i;i=nxt[i])
		{
			int v=to[i];
			if(c[i]&&num[v]==-1)
			{
				num[v]=num[u]+1;
				q.push(v);
			}
		}
	}
	return num[t]!=-1;
}

int dfs(int u,int minflow)
{
	if(!minflow||u==t) return minflow;
	int preflow=0,nowflow;
	for(int i=cur[u];i;i=nxt[i])
	{
		cur[u]=i;
		int v=to[i];
		if(num[v]==num[u]+1&&(nowflow=dfs(v,min(minflow-preflow,c[i]))))
		{
			preflow+=nowflow;
			c[i]-=nowflow;
			c[i^1]+=nowflow;
			if(!(minflow-preflow)) break;
		}
	}
	return preflow;
}

int dinic()
{
	int maxflow=0;
	while(bfs())
		maxflow+=dfs(s,INF);
	return maxflow;
}

int main()
{
	scanf("%d%d",&n,&m);
	s=1,t=1+n+m+1;
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch[i]+1);
		for(int j=1;j<=m;j++)
		{
			if(ch[i][j]=='o')
			{
				adde(1+i,1+n+j,1);
				adde(1+n+j,1+i,1);
			}
			if(ch[i][j]=='S')
			{
				stx=i,sty=j;
				adde(s,1+i,INF);
				adde(s,1+n+j,INF);
			}
			if(ch[i][j]=='T')
			{
				edx=i,edy=j;
				adde(1+i,t,INF);
				adde(1+n+j,t,INF);
			}
		}
	}
	if(stx==edx||sty==edy)
	{
		puts("-1");
		return 0;
	}
	printf("%d\n",dinic());
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值