UVA-10480 Sabotage(最大流最小割定理+輸出路徑)

題目鏈接:UVA-10480 Sabotage

The regime of a small but wealthy dictatorship has been abruptly overthrown by an unexpected rebellion.
Because of the enormous disturbances this is causing in world economy, an imperialist military
super power has decided to invade the country and reinstall the old regime.
For this operation to be successful, communication between the capital and the largest city must
be completely cut. This is a difficult task, since all cities in the country are connected by a computer
network using the Internet Protocol, which allows messages to take any path through the network.
Because of this, the network must be completely split in two parts, with the capital in one part and
the largest city in the other, and with no connections between the parts.
There are large differences in the costs of sabotaging different connections, since some are much
more easy to get to than others.
Write a program that, given a network specification and the costs of sabotaging each connection,
determines which connections to cut in order to separate the capital and the largest city to the lowest possible cost.

Input

Input file contains several sets of input. The description of each set is given below.
The first line of each set has two integers, separated by a space: First one the number of cities, n in
the network, which is at most 50. The second one is the total number of connections, m, at most 500.
The following m lines specify the connections. Each line has three parts separated by spaces: The
first two are the cities tied together by that connection (numbers in the range 1 − n). Then follows the cost of cutting the connection (an integer in the range 1 to 40000000). Each pair of cites can appear at most once in this list.
Input is terminated by a case where values of n and m are zero. This case should not be processed.
For every input set the capital is city number 1, and the largest city is number 2.

Output

For each set of input you should produce several lines of output. The description of output for each set of input is given below:
The output for each set should be the pairs of cities (i.e. numbers) between which the connection
should be cut (in any order), each pair on one line with the numbers separated by a space. If there is more than one solution, any one of them will do.
Print a blank line after the output for each set of input.

Sample Input

5 8
1 4 30
1 3 70
5 3 20
4 3 5
4 5 15
5 2 10
3 2 25
2 4 50
5 8
1 4 30
1 3 70
5 3 20
4 3 5
4 5 15
5 2 10
3 2 25
2 4 50
0 0

Sample Output

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


題意:

含有n個結點及m條無向邊的圖,每條邊都帶有邊權(一個正整數),現要刪除邊使得結點s(編號爲1)和t(編號爲2)不連通,要令刪除的邊權之和最小,要求輸出被刪除的邊(以任意順序輸出任意一個滿足的方案)



分析:

這是一個最小割問題,由最大流最小割定理可知:s-t 最小割的容量等於 s-t 最大流的流量

那麼求最小割就轉化爲了求最大流,但是要注意,求最小割時是無向圖,所以正向邊和反向邊的初始殘量都要等於邊權

這題有一個難點,並不是要求輸出最小割,而是輸出達到最小割時需要割掉的邊

因爲跑了一遍最大流後,圖會被分成兩部分(結點s所在的部分和結點t所在的部分),所以只需要先預處理一下哪些點所在部分不同,然後遍歷所有邊,起點、終點所在部分不同的邊即是需要割掉的邊。(爲方便處理,建圖時要記錄邊的起點和終點)



以下代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<map>
#include<algorithm>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e2+50;
const int maxm=1e3+50;
struct edge
{
    int w;
    int from;
    int to;
    int next;
}e[maxm];
int head[maxn],cnt;
int s,t;
int n,m;
void addedge(int u,int v,int w)
{
    e[cnt].from=u;
    e[cnt].w=w;
    e[cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt;
    cnt++;
}
int dis[maxn];     //dis數組記錄層次
bool bfs()         //利用BFS建立分成圖,從而可以多次DFS增廣
{
	memset(dis,-1,sizeof(dis));     //初始化dis數組
	queue<int> q;
	q.push(s);
	dis[s]=0;      //源點層次爲0
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i!=-1;i=e[i].next)
		{
			int v=e[i].to;
			if(e[i].w>0&&dis[v]==-1)     //可達&&未分層
			{
				dis[v]=dis[u]+1;         //分層
				if(v==t)                 //若到達匯點,則分層結束,返回true
					return true;
				q.push(v);
			}
		}
	}
	return false;  //運行到此處,說明匯點已不可達,返回false
}
int cur[maxn];     //弧優化:cur數組用於記錄上一次DFS增廣時u已經增廣到第幾條邊,從而優化時間
int dfs(int u,int flow)       //flow代表流入u點的最大流量
{
	if(u==t)
		return flow;          //到達匯點,直接返回flow
	for(int &i=cur[u];i!=-1;i=e[i].next)
	{                         //注意i前面用&引用,這樣就可以直接改變cur[u]
		int v=e[i].to;
		if(dis[v]==dis[u]+1&&e[i].w>0)   //v爲u的下一層&&可達
		{
			int k=dfs(v,min(flow,e[i].w));
			if(k>0)
			{
				e[i].w-=k;         //正向邊-=k
				e[i^1].w+=k;       //反向邊+=k
				return k;
			}
		}
	}
	return 0;       //無法繼續增廣,返回0
}

int dinic()
{
	int ans=0;      //記錄總流量
	while(bfs())    //分層
	{
		for(int i=0;i<maxn;i++)    //初始化cur數組,即將head數組賦給cur數組
			 cur[i]=head[i];
		while(int k=dfs(s,INF))    //增廣
			ans+=k;
	}
	return ans;
}
bool vis[maxn];
void dfs_again(int u)
{
    if(vis[u])
        return;
    else
        vis[u]=true;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        if(e[i].w>0)        //殘量>0才能通過
            dfs_again(e[i].to);
    }
}
int main()
{
    while(scanf("%d %d",&n,&m)&&n+m)
    {
        memset(head,-1,sizeof(head));
        cnt=0;
        s=1;
        t=2;
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d %d %d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        dinic();
        memset(vis,false,sizeof(vis));
        dfs_again(s);            //遍歷一遍s所在部分,加上標記
        bool flag[maxm]={false};
        for(int i=0;i<cnt;i++)   //遍歷所有邊
        {
            if(flag[i])
                continue;
            int u=e[i].from,v=e[i].to;
            if((!vis[u]&&vis[v])||(!vis[v]&&vis[u]))
            {
                printf("%d %d\n",u,v);
                flag[i]=true;
                flag[i^1]=true;  //防止多輸出一次反向邊
            }
        }
        printf("\n");
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章