网络流练习回顾

背景:

本来好像今天继续做DP来着,某人把我拉来被网络流虐。。

其实感觉网络流考的不是这个算法,考的是你能想到这题是网络流,而我今天也做了\(5\)道题,因为知道是网络流的题,感觉就是把建边改改就A了 (虽然有一题De了一下午Bug

算了,废话不多说,讲题吧


P3701 伪模板」主席树

又是一道文字题 (本蒟蒻看错题\(5\)

这题其实不难(主要知道是网络流的题,

从源点向每一个\(byx\)的人连一条边,边的容量为这个人的寿命,然后从这个人向他可以打败的人连一条容量为\(1\)的边,因为每两个人只能比一场,最后从诗乃酱的人向汇点连一条容量为此人寿命的边。

注意,YYY是可以给J续寿命的,而且自己的寿命不会减少,所以就算这个YYY的寿命是\(0\),也可以给J续命。

然后?然后就跑最大流了,这个应该不用说了。。

手起,码落:

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define re register
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
const int N=21500;
struct edge{int v,net,val;}e[N];
int n,m,s,a[N],b[N],t,ans,s1,cnt=1,hd[N],num1,num2,dep[N],cur[N];
bool KO[10][10];
inline void add(int u,int v,int val)
{
	e[++cnt].v=v,e[cnt].net=hd[u],e[cnt].val=val,hd[u]=cnt;
	e[++cnt].v=u,e[cnt].net=hd[v],e[cnt].val=0,hd[v]=cnt;
}
string ch;
queue <int> q;
bool bfs()
{
	for(;!q.empty();q.pop());
	memset(dep,0,sizeof(dep));
	for(re int i=0;i<=(n<<1)+2;i++)cur[i]=hd[i];
	dep[s]=1,q.push(s);
	for(re int u;!q.empty();)
	{
		u=q.front(),q.pop();
		for(re int v,i=hd[u];i;i=e[i].net)
		{
			v=e[i].v;
			if(e[i].val==0||dep[v])continue;
			dep[v]=dep[u]+1;
			if(v==t)return 1;
			q.push(v);
		}
	}
	return 0;
}
int dinic(int u,int flow)
{
	if(u==t) return flow;
	re int rest=flow;
	for(re int i=cur[u],v,k;i;i=e[i].net)
	{
		v=e[i].v;cur[u]=i;
		if(e[i].val==0||dep[v]!=dep[u]+1)continue;
		k=dinic(v,min(e[i].val,rest));
		e[i].val-=k,e[i^1].val+=k;
		rest-=k;
	}
	return flow-rest;
}
int main()
{
	KO[1][2]=KO[1][3]=KO[2][4]=KO[2][5]=KO[3][2]=KO[3][5]=KO[4][3]=KO[4][1]=KO[5][1]=KO[5][4]=1;
	scanf("%d%d",&n,&m);
	s1=(n<<1)+1,t=(n<<1)+2;
	add(s,s1,m);
	for(re int i=1;i<=n;i++)
	{
		cin>>ch;
		if(ch=="J")a[i]=1;
		else if(ch=="W")a[i]=2;
		else if(ch=="HK")a[i]=3;
		else if(ch=="YYY")a[i]=4,num1++;
		else a[i]=5;
	}
	for(re int i=1;i<=n;i++)
	{
		cin>>ch;
		if(ch=="J")b[i]=1;
		else if(ch=="W")b[i]=2;
		else if(ch=="HK")b[i]=3;
		else if(ch=="YYY")b[i]=4,num2++;
		else b[i]=5;
	}
	for(re int i=1,val;i<=n;i++)
	{
		scanf("%d",&val);
		if(a[i]==1) add(s1,i,val+num1);
		else add(s1,i,val);
		for(re int j=1;j<=n;j++)
			if(KO[a[i]][b[j]])add(i,j+n,1);
	}
	for(re int i=1,val;i<=n;i++)
	{
		scanf("%d",&val);
		if(b[i]==1) add(i+n,t,val+num2);
		else add(i+n,t,val);
	}
	for(;bfs();ans+=dinic(s,inf));
	printf("%d",ans);
	return 0;
}

P1231 教辅的组成

这题虽然评分是省选,其实可以算最大流的模板题了,很适合刚学最大流的OIer做(至少比上一题简单

因为输入的是书与作业本,答案的可能匹配,所以在网络流分层时,可以吧书放在中间,然后再建开始边。

水的网络流题建边也差不多,先从汇点向作业本建容量为\(1\)的边,因为每一本作业本只能匹配一次,然后就是再向书,再向答案,最后向汇点建边。

不过要注意的是,因为每一本书只能匹配一次,所以要把书的节点拆成两个,中间建一条容量为\(1\)的边.

又没了,上代码吧(后面两题除了建边都一样就不copy代码了

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define re register
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
const int N=41005,M=140005;
struct edge{int v,net,val;}e[M];
int n1,n2,n3,m1,m2,s,t,ans,cnt=1,hd[N],dep[N],cur[N];
inline void add(int u,int v,int val)
{
	e[++cnt].v=v,e[cnt].net=hd[u],e[cnt].val=val,hd[u]=cnt;
	e[++cnt].v=u,e[cnt].net=hd[v],e[cnt].val=0,hd[v]=cnt;
}
string ch;
queue <int> q;
bool bfs()
{
	for(;!q.empty();q.pop());
	memset(dep,0,sizeof(dep));
	for(re int i=0;i<=n1+n1+n2+n3+1;i++)cur[i]=hd[i];
	dep[s]=1,q.push(s);
	for(re int u;!q.empty();)
	{
		u=q.front(),q.pop();
		for(re int v,i=hd[u];i;i=e[i].net)
		{
			v=e[i].v;
			if(e[i].val==0||dep[v])continue;
			dep[v]=dep[u]+1;
			if(v==t)return 1;
			q.push(v);
		}
	}
	return 0;
}
int dinic(int u,int flow)
{
	if(u==t) return flow;
	re int rest=flow;
	for(re int i=cur[u],v,k;i;i=e[i].net)
	{
		v=e[i].v;cur[u]=i;
		if(e[i].val==0||dep[v]!=dep[u]+1)continue;
		k=dinic(v,min(e[i].val,rest));
		e[i].val-=k,e[i^1].val+=k;
		rest-=k;
	}
	return flow-rest;
}
int main()
{
//	freopen("P1231.in","r",stdin);
	scanf("%d%d%d",&n1,&n2,&n3);
	scanf("%d",&m1),t=n1+n2+n3+n1+1;
	for(re int i=1;i<=n2;i++)add(s,i+n1,1);
	for(re int i=1;i<=n3;i++)add(i+n1+n2,t,1);
	for(re int i=1;i<=n1;i++)add(i,i+n1+n2+n3,1);
	for(re int i=1,u,v;i<=m1;i++)
	{
		scanf("%d%d",&u,&v);
		add(v+n1,u,1);
	}
	scanf("%d",&m2);
	for(re int i=1,u,v;i<=m2;i++)
	{
		scanf("%d%d",&u,&v);
		add(u+n1+n2+n3,v+n1+n2,1);
	}
	for(;bfs();ans+=dinic(s,inf));
	printf("%d",ans);
	return 0;
}

P2598 [ZJOI2009]狼和羊的故事

这题需要一点思考,而且不会一眼看出来是一道网络流。

先把地图转化一下,变成一个有\(n\times m\)个节点的网络图,每个节点向四周连边,而我们的答案该怎么求?用围栏可以看成把这条边割掉,而答案就是求最小割的大小。

等等,最小割?

我们好像刚学最大流是就知道了,最大流等于最小割,所以把羊或者狼的点连上源点,另外一个连上汇点,跑最大流就好了,不是任何动物的领地的点不用管。

然后,我们又快乐的A掉了一题!


P2472 [SCOI2007]蜥蜴

这题又比较基础了,从每个点向它能到达的点连边,然后从源点向有蜥蜴的石柱连边,最后那些石柱可以一步跳出地图,就把它们连向汇点。

但这题也要拆点,把每个点拆成两个,中间连一条容量为石柱高度的边,表示最多能通过多少条蜥蜴,然后跑最大流了。


好像都挺水的哩
posted @ 2020-08-28 08:21  Chester1011  阅读(140)  评论(0编辑  收藏  举报
/*simplememory*/