zoukankan      html  css  js  c++  java
  • POJ 2135 Farm Tour (网络流,最小费用最大流)

    POJ 2135 Farm Tour (网络流,最小费用最大流)

    Description

    When FJ's friends visit him on the farm, he likes to show them around. His farm comprises N (1 <= N <= 1000) fields numbered 1..N, the first of which contains his house and the Nth of which contains the big barn. A total M (1 <= M <= 10000) paths that connect the fields in various ways. Each path connects two different fields and has a nonzero length smaller than 35,000.

    To show off his farm in the best way, he walks a tour that starts at his house, potentially travels through some fields, and ends at the barn. Later, he returns (potentially through some fields) back to his house again.

    He wants his tour to be as short as possible, however he doesn't want to walk on any given path more than once. Calculate the shortest tour possible. FJ is sure that some tour exists for any given farm.

    Input

    Line 1: Two space-separated integers: N and M.

    Lines 2..M+1: Three space-separated integers that define a path: The starting field, the end field, and the path's length.

    Output

    A single line containing the length of the shortest tour.

    Sample Input

    4 5
    1 2 1
    2 3 1
    3 4 1
    1 3 2
    2 4 2

    Sample Output

    6

    Http

    POJ:https://vjudge.net/problem/POJ-2135

    Source

    图论,网络流,最小费用最大流

    题目大意

    给定一个n个点m条边的无向图,求两条不相交的从1到n的最短路径。

    解决思路

    看到这道题目时,首先想到的是最短路径的算法,但显然不是跑两边Dijkstra或spfa,想要两条路一起走也不科学,所以我们想到了网络流算法。
    想一想,我们要求两条不相交的从1到n的最短路径,若假设我们把所有的边看作流量为1的边,那么这是不是要求从1到n容量为2的流呢?所以我们可以想到有如下的算法:
    对于原来的边上的权值“距离”,我们将其换一个定义:花费。另外再给每一个边赋上1的流量。同时,为了控制1点流出的和n点汇入的流不超过2,我们再设一个超级源点和超级汇点,在超级源点与1之间连流量为2的边,而在n与超级汇点之间连流量为2的边。然后,我们就可以用最小费用最大流来解决了。
    需要注意的是,这道题目的边是无向边,所以我们在网络流连边时也要连无向边,所以本题不能用邻接矩阵来存图,而要使用邻接表的形式

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<queue>
    using namespace std;
    
    const int maxN=5001;
    const int maxM=50001;
    const int inf=2147483647;
    
    class Edge
    {
    public:
    	int u,v,flow,cost;//记录每一条边的信息,出点,目的点,残量,花费
    };
    
    int n,m;
    int cnt=-1;//记录邻接表的边数
    int Head[maxN];
    int Next[maxM];
    Edge E[maxM];
    int Flow[maxN];//spfa中保存每个点可以通过的残量
    int Pre[maxN];//spfa中保存每个点是由哪一条边转移过来的边的标号
    int Dist[maxN];//spfa中保存到每个点的距离,即最小花费
    bool inqueue[maxN];
    
    void Add_Edge(int u,int v,int flow,int cost);//添加边
    void _Add(int u,int v,int flow,int cost);
    bool spfa();
    
    int main()
    {
    	cin>>n>>m;
    	memset(Head,-1,sizeof(Head));
    	memset(Next,-1,sizeof(Next));
    	for (int i=1;i<=m;i++)
    	{
    		int u,v,w;
    		scanf("%d%d%d",&u,&v,&w);
    		Add_Edge(u,v,1,w);//注意,正反边都要连
    		Add_Edge(v,u,1,w);
    	}
    	Add_Edge(0,1,2,0);//连接源点与1
    	Add_Edge(n,n+1,2,0);//连接n与汇点
    	int Ans=0;//记录花费
    	while (spfa())//spfa寻增广路
    	{
    		int now=n+1;
    		int last=Pre[now];//从汇点向回走,将增广路上的每一条边均减去消耗的流量
    		while (now!=0)
    		{
    			E[last].flow-=Flow[n+1];
    			E[last^1].flow+=Flow[n+1];
    			now=E[last].u;
    			last=Pre[now];
    		}
    		Ans+=Dist[n+1]*Flow[n+1];//累计花费
    	}
    	cout<<Ans<<endl;
    }
    
    void Add_Edge(int u,int v,int flow,int cost)
    {
    	_Add(u,v,flow,cost);//每一次加边的同时,加入其反向边,反向边的残量为0,花费为-cost
    	_Add(v,u,0,-cost);
    	return;
    }
    
    void _Add(int u,int v,int flow,int cost)
    {
    	cnt++;
    	Next[cnt]=Head[u];
    	Head[u]=cnt;
    	E[cnt].u=u;
    	E[cnt].v=v;
    	E[cnt].flow=flow;
    	E[cnt].cost=cost;
    	return;
    }
    
    bool spfa()
    {
    	memset(Pre,-1,sizeof(Pre));//前驱边的编号
    	memset(inqueue,0,sizeof(inqueue));
    	memset(Flow,0,sizeof(Flow));
    	memset(Dist,127,sizeof(Dist));
    	queue<int> Q;
    	while (!Q.empty())
    		Q.pop();
    	Q.push(0);//将源点放入队列
    	Dist[0]=0;
    	Flow[0]=inf;
    	inqueue[0]=1;
    	do
    	{
    		int u=Q.front();
    		//cout<<u<<endl;
    		inqueue[u]=0;
    		Q.pop();
    		for (int i=Head[u];i!=-1;i=Next[i])
    		{
    			int v=E[i].v;
    			if ((E[i].flow>0)&&(Dist[u]+E[i].cost<Dist[v]))//当还有残量存在且花费更小时,修改v的信息
    			{
    				Dist[v]=E[i].cost+Dist[u];
    				Pre[v]=i;
    				Flow[v]=min(Flow[u],E[i].flow);
    				if (inqueue[v]==0)
    				{
    					Q.push(v);
    					inqueue[v]=1;
    				}
    			}
    		}
    	}
    	while (!Q.empty());
    	if (Pre[n+1]==-1)//当汇点没有前驱,及说明没有增广到汇点,也说明不存在增广路,直接退出
    		return 0;
    	return 1;
    }
    
  • 相关阅读:
    C 和 C++ 的标准库分别有自己的 locale 操作方法,C 标准库的 locale 设定函数是 setlocale(),而 C++ 标准库有 locale 类和流对象的 imbue() 方法(gcc使用zh_CN.GBK,或者zh_CN.UTF-8,VC++使用Chinese_People's Republic of China.936或者65001.)
    QCache 缓存(模板类,类似于map,逻辑意义上的缓存,方便管理,和CPU缓存无关。自动获得被插入对象的所有权,超过一定数量就会抛弃某些值)
    QBuffer简单操作(被看做一个标准的可随机访问的文件,支持信号)
    Qt里的原子操作QAtomicInteger
    进程、线程、协程、例程、过程
    net Core 2.2
    如何看源码
    code review规则
    NET Core中使用Dapper操作Oracle存储过程
    实现一个Promise
  • 原文地址:https://www.cnblogs.com/SYCstudio/p/7261255.html
Copyright © 2011-2022 走看看