• 网络流练习回顾


    背景:

    本来好像今天继续做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 imes m)个节点的网络图,每个节点向四周连边,而我们的答案该怎么求?用围栏可以看成把这条边割掉,而答案就是求最小割的大小。

    等等,最小割?

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

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


    P2472 [SCOI2007]蜥蜴

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

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


    好像都挺水的哩
  • 相关阅读:
    ceph 网络配置
    Centos7.2 下DNS+NamedManager高可用部署方案完整记录
    Mysql多实例数据库
    Mysql 基础
    搭建本地YUM仓库
    Go实现线程安全的缓存
    KubeEdge安装详细教程
    Kubeedge实现原理
    Go语言中new()和make()的区别
    Go语言中append()函数的源码实现在哪里?
  • 原文地址:https://www.cnblogs.com/jkzcr01-QAQ/p/13575229.html
Copyright © 2020-2023  润新知