zoukankan      html  css  js  c++  java
  • 【BZOJ2502】清理雪道(最大费用最大流)

    上下界网络流的做法大佬们都讲过了,我就来讲一个另类的解法。(不过也是网络流)

    这个做法是考场上想了很久都不会后奇思妙想想出来的(不会上下界网络流),所以没多少思路引导。

    首先原题明显可以转移成一个类似最小链覆盖的问题。

    剩下的就是我的具体做法:

    我们对原来的有向无环图进行改造:

    上面的是原图,我们把图中的每一条边 e i e_i ei 拆成两条边:一条流量为 1 1 1,费用为 1 1 1,不妨取名叫 a i a_i ai;一条流量为 ∞ infty ,费用为 0 0 0,取名叫 b i b_i bi。然后源点向每个入度为 0 0 0 的点(也就是链覆盖的起点)连边,流量 ∞ infty ,费用 0 0 0;每个出度为 0 0 0 的点(也就是链覆盖的终点)向汇点连边,流量 ∞ infty ,费用 0 0 0

    然后跑最大费用最大流。

    意思就是说每次增广时,相当于我新覆盖一条链。在增广中:

    如果某条原图中的边 e i e_i ei 没有被走过,那么为了满足最大费用,程序肯定会选择流费用为 1 1 1 的那条边 a i a_i ai,费用增加 1 1 1,代表我新清理了一条雪道,即新访问了一条原图中的边;

    如果这条边 e i e_i ei 被走过了,那么费用为 1 1 1 的那条边肯定被流满了,只能流费用为 0 0 0 的边,不贡献费用,意思就是我走过的这条边在我前几次覆盖时已经被覆盖了,也就是说我只是路过这,并不要清理这。

    然后根据最大费用最大流的特性,每次增广完后,能保证所得的解是当前的最优解,也就是说它能保证用最优方法覆盖所有边。

    那么当贡献的费用等于总边数时,整个图也就被我们覆盖完了,答案就是链的条数,也就是覆盖的次数,或者说增广的次数。

    整道题也就结束了。

    顺带说一下,其实这种跑网络流却不跑完,把网络流当成一个类似贪心的工具来使用的 trick 挺实用也挺常见的(类似数学中的设而不求),比如 BZOJ2893征服王 也可以用这个 trick 做,而且比这个麻烦一点,所以建议总结一下(

    代码如下:

    #include<bits/stdc++.h>
    
    #define N 110
    #define M 10010
    #define INF 0x7fffffff
    
    using namespace std;
    
    inline int read()
    {
    	int x=0,f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		x=(x<<1)+(x<<3)+(ch^'0');
    		ch=getchar();
    	}
    	return x*f;
    }
    
    int n,s,t,tot,ans;
    int rudu[N],chudu[N];
    int cnt=1,head[N],to[N*4+M*4],nxt[N*4+M*4],c[N*4+M*4],w[N*4+M*4];
    int pre[N],dis[N];
    bool inq[N];
    
    queue<int>q;
    
    void adde(int u,int v,int ci,int wi)
    {
    	to[++cnt]=v;
    	c[cnt]=ci;
    	w[cnt]=wi;
    	nxt[cnt]=head[u];
    	head[u]=cnt;
    	
    	to[++cnt]=u;
    	c[cnt]=0;
    	w[cnt]=-wi;
    	nxt[cnt]=head[v];
    	head[v]=cnt;
    }
    
    bool SPFA()
    {
    	memset(dis,128,sizeof(dis));
    	q.push(s);
    	dis[s]=0;
    	inq[s]=1;
    	while(!q.empty())
    	{
    		int u=q.front();
    		q.pop();
    		inq[u]=0;
    		for(int i=head[u];i;i=nxt[i])
    		{
    			int v=to[i];
    			if(c[i]&&dis[u]+w[i]>dis[v])
    			{
    				dis[v]=dis[u]+w[i];
    				pre[v]=i;
    				if(!inq[v])
    				{
    					inq[v]=1;
    					q.push(v);
    				}
    			}
    		}
    	}
    	return dis[t]!=dis[0];
    }
    
    void MCMF()
    {
    	int maxcost=0,maxflow=0;
    	while(SPFA())
    	{
    		int minflow=INF;
    		for(int u=t;u!=s;u=to[pre[u]^1]) 
    			minflow=min(minflow,c[pre[u]]);
    		for(int u=t;u!=s;u=to[pre[u]^1])
    		{
    			c[pre[u]]-=minflow;
    			c[pre[u]^1]+=minflow;
    			maxcost+=minflow*w[pre[u]];
    		}
    		maxflow+=minflow;
    		ans++;//统计增广次数(链的条数)
    		if(maxcost==tot) break;//如果最大费用达到总边数
    	}
    }
    
    int main()
    {
    	n=read();
    	s=1,t=1+n+1;
    	for(int i=1;i<=n;i++)
    	{
    		int m=read();
    		tot+=m;
    		for(int j=1;j<=m;j++)
    		{
    			int v=read();
    			adde(1+i,1+v,1,1);//ai
    			adde(1+i,1+v,INF,0);//bi
    			chudu[i]++,rudu[v]++;
    		}
    	}
    	for(int i=1;i<=n;i++)
    	{
    		if(!rudu[i]) adde(s,1+i,INF,0);//源点连入度为0的点
    		if(!chudu[i]) adde(1+i,t,INF,0);//出度为0的点连汇点
    	}
    	MCMF();//最大费用最大流
    	printf("%d
    ",ans);
    	return 0;
    }
    /*
    5
    1 2
    0
    2 2 4
    0
    1 4
    */
    
  • 相关阅读:
    PyQt作品 – PingTester – 多点Ping测试工具
    关于和技术人员交流的一二三
    Pyjamas Python Javascript Compiler, Desktop Widget Set and RIA Web Framework
    Hybrid Qt applications with PySide and Django
    pyjamas build AJAX apps in Python (like Google did for Java)
    PyQt 维基百科,自由的百科全书
    InfoQ:请问为什么仍要选择Java来处理后端的工作?
    Eric+PyQt打造完美的Python集成开发环境
    python select module select method introduce
    GUI Programming with Python: QT Edition
  • 原文地址:https://www.cnblogs.com/ez-lcw/p/14448659.html
Copyright © 2011-2022 走看看