UVA 10480 Sabotage (网络流,最大流,最小割)

UVA 10480 Sabotage (网络流,最大流,最小割)

Description

The regime of a small but wealthy dictatorship has been abruptly overthrown by an unexpected rebel-lion. 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 speci cation 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

Http

https://vjudge.net/problem/UVA-10480

Source

网络流,最大流,最小割

题目大意

在给定的无向图中,割去权值和最小的边使得1和2不连通

解决思路

根据最小割最大流定理,最大流即为最小割。所以本题的最小割是可以很快求出来的,关键是如何输出割去哪些边。
因为在执行完最大流算法后,剩下的残量网络图是把S和T割开的,图中的点组成S和T两个集合,所以可以用一遍dfs求出与S在同一边的点,然后从这些点出发,寻找与其相连的不与S相连的点,即跨越了集合S和集合T的边,这些边就是要割掉的。
这里使用Dinic算法实现最大流,可以参考这篇文章

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxN=60;
const int maxM=2000;
const int inf=2147483647;

class Edge
{
public:
    int u,v,flow;
    bool edge;
};

int n,m;
int cnt;
int Head[maxN];
int Next[maxM];
Edge E[maxM];
int depth[maxN];
int cur[maxN];
int Q[maxM];
bool is_outp[maxN][maxN];
bool setS[maxN];

void Add_Edge(int u,int v,int flow);
bool bfs();
int dfs(int u,int flow);
void outp(int u);

int main()
{
    bool op=0;//用来标记是否是第一组数据,因为要在每两组数据之间输出一个空行
    while (cin>>n>>m)
    {
        if ((n==0)&&(m==0))
            break;
        if (op)
            printf("\n");
        op=1;
        cnt=-1;//初始化为-1
        memset(Head,-1,sizeof(Head));
        for (int i=1;i<=m;i++)
        {
            int u,w,v;
            scanf("%d%d%d",&u,&v,&w);//连边
            Add_Edge(u,v,w);
            Add_Edge(v,u,w);
        }
        while (bfs())//求解最大流
        {
            for (int i=1;i<=n;i++)
                cur[i]=Head[i];
            while (dfs(1,inf));
        }
        memset(is_outp,0,sizeof(is_outp));//标记某对点是否已经输出
        memset(setS,0,sizeof(setS));//标记某个点是否在集合S中
        outp(1);//dfs求在集合S中的点
        for (int i=1;i<=n;i++)
            if (setS[i]==1)
            {
                for (int j=Head[i];j!=-1;j=Next[j])//寻找横跨两个集合的边
                {
                    int v=E[j].v;
                    if ((setS[v]==0)&&(is_outp[i][v]==0))//注意判重
                    {
                        printf("%d %d\n",i,v);
                        is_outp[i][v]=1;
                    }
                }
            }
        /*
        for (int i=0;i<=cnt;i++)
            if ((E[i].flow==0)&&(E[i].edge==1))
                printf("%d %d\n",E[i].u,E[i].v);
        //*/
    }
}

void Add_Edge(int u,int v,int flow)
{
    cnt++;
    Next[cnt]=Head[u];
    Head[u]=cnt;
    E[cnt].u=u;
    E[cnt].v=v;
    E[cnt].flow=flow;
    E[cnt].edge=1;

    cnt++;
    Next[cnt]=Head[v];
    Head[v]=cnt;
    E[cnt].v=u;
    E[cnt].u=v;
    E[cnt].flow=0;
    E[cnt].edge=0;
}

bool bfs()
{
    memset(depth,-1,sizeof(depth));
    int h=1,t=0;
    Q[1]=1;
    depth[1]=1;
    do
    {
        t++;
        int u=Q[t];
        for (int i=Head[u];i!=-1;i=Next[i])
        {
            int v=E[i].v;
            if ((depth[v]==-1)&&(E[i].flow>0))
            {
                depth[v]=depth[u]+1;
                h++;
                Q[h]=v;
            }
        }
    }
    while (h!=t);
    if (depth[2]==-1)
        return 0;
    /*for (int i=1;i<=n;i++)
        cout<<depth[i]<<" ";
    cout<<endl;
    getchar();*/
    return 1;
}

int dfs(int u,int flow)
{
    //cout<<u<<endl;
    if (u==2)
        return flow;
    for (int &i=cur[u];i!=-1;i=Next[i])
    {
        int v=E[i].v;
        if ((depth[v]==depth[u]+1)&&(E[i].flow>0))
        {
            //cout<<i<<" "<<u<<' '<<v<<endl;
            //cout<<"E:"<<E[i].u<<" "<<E[i].v<<" "<<E[i].flow<<endl;
            //getchar();
            int di=dfs(v,min(flow,E[i].flow));
            if (di>0)
            {
                E[i].flow-=di;
                E[i^1].flow+=di;
                return di;
            }
        }
    }
    return 0;
}

void outp(int u)
{
    setS[u]=1;
    for (int i=Head[u];i!=-1;i=Next[i])
        if ((setS[E[i].v]==0)&&(E[i].flow>0))//注意要流量大于0
            outp(E[i].v);
    return;
}
posted @ 2017-08-17 19:55 SYCstudio 阅读(...) 评论(...) 编辑 收藏