POJ 1815 Friendship(字典序最小最小割)

来源:互联网 发布:sql 不等于 编辑:程序博客网 时间:2024/05/16 06:44

Friendship
Time Limit: 2000MS Memory Limit: 20000KTotal Submissions: 10805 Accepted: 2996

Description

In modern society, each person has his own friends. Since all the people are very busy, they communicate with each other only by phone. You can assume that people A can keep in touch with people B, only if 
1. A knows B's phone number, or 
2. A knows people C's phone number and C can keep in touch with B. 
It's assured that if people A knows people B's number, B will also know A's number. 

Sometimes, someone may meet something bad which makes him lose touch with all the others. For example, he may lose his phone number book and change his phone number at the same time. 

In this problem, you will know the relations between every two among N people. To make it easy, we number these N people by 1,2,...,N. Given two special people with the number S and T, when some people meet bad things, S may lose touch with T. Your job is to compute the minimal number of people that can make this situation happen. It is supposed that bad thing will never happen on S or T. 

Input

The first line of the input contains three integers N (2<=N<=200), S and T ( 1 <= S, T <= N , and S is not equal to T).Each of the following N lines contains N integers. If i knows j's number, then the j-th number in the (i+1)-th line will be 1, otherwise the number will be 0. 

You can assume that the number of 1s will not exceed 5000 in the input. 

Output

If there is no way to make A lose touch with B, print "NO ANSWER!" in a single line. Otherwise, the first line contains a single number t, which is the minimal number you have got, and if t is not zero, the second line is needed, which contains t integers in ascending order that indicate the number of people who meet bad things. The integers are separated by a single space. 

If there is more than one solution, we give every solution a score, and output the solution with the minimal score. We can compute the score of a solution in the following way: assume a solution is A1, A2, ..., At (1 <= A1 < A2 <...< At <=N ), the score will be (A1-1)*N^t+(A2-1)*N^(t-1)+...+(At-1)*N. The input will assure that there won't be two solutions with the minimal score. 

Sample Input

3 1 31 1 01 1 10 1 1

Sample Output

12

Source

POJ Monthly

题目大意:

    给一些人之间的单线联系关系,问要使一个人不能联系到另一个人至少要去掉几个人,输出这几个人,多组解要求的时候要求字典序最小。


解题思路:

    如果不要求字典序最小,那么这题就是一个很简单的最小割,每个人拆成一个入点,一个出点,连一个容量为1的边,关系连上容量为无穷大边。

    但是,由于最大流增广的特点,我们没有办法求最大流的时候使得字典序最小,那么我们就只能从小到大枚举删除的一个人内部的边。很多人都是枚举每次跑一边最大流写的,不过这样重复操作太多了,我们只改变了一条边,没必要完全重跑最大流。

  如果它本来就没有流量,那么去掉它是无所谓的,所以他就不是割上的边。对于它已经满流的情况,如果可以在现在的网络上找到一条其它的从这条边的起点到终点的增广路,那么说明如果我去掉这条边,还是可以换一条路流,不影响最大流,所以这时它也不是割上的边。最后一种情况,也就是,它已经满流,并且找不到可以代替它的增广路,那么它就是割上的点。我们删掉它就要退流量了,从增广形成的图上,找一条从边起点到源点的路径,一条从汇点到边终点的路径,然后增广,就可以退掉这条边的流量。

    这样我们只通过几次dfs就可以修改图了,降低一个N的复杂度。


AC代码:

#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#include <vector>#include <queue>#include <stack>#include <cmath>#include <cstdlib>#include <string>#include <map>#include <ctime>using namespace std;#define INF 0x3f3f3f3f#define LL long long#define fi first#define se second#define mem(a,b) memset((a),(b),sizeof(a))const int MAXN=200+3;const int MAXV=MAXN*2;int N,S,T,V;int G[MAXV][MAXV];//原图int lG[MAXV][MAXV];//由于增广,形成的图int level[MAXV];int iter[MAXV];bool vis[MAXV];void init(){    mem(G,0);    mem(lG,0);}void add_edge(int from,int to,int cap){    G[from][to]+=cap;}void bfs(int s){    mem(level,-1);    queue<int> que;    level[s]=0;    que.push(s);    while(!que.empty())    {        int u=que.front(); que.pop();        for(int v=0;v<V;++v)        {            if(G[u][v]>0&&level[v]<0)            {                level[v]=level[u]+1;                que.push(v);            }        }    }}int dfs(int u,int t,int f){    if(u==t)        return f;    for(int &v=iter[u];v<V;++v)    {        if(G[u][v]>0&&level[u]<level[v])        {            int d=dfs(v,t,min(f,G[u][v]));            if(d>0)            {                G[u][v]-=d;//同时对两个图增广                G[v][u]+=d;                lG[u][v]-=d;                lG[v][u]+=d;                return d;            }        }    }    return 0;}int dinic(int s,int t){    int flow=0;    for(;;)    {        bfs(s);        if(level[t]<0)            return flow;        mem(iter,0);        int f;        while((f=dfs(s,t,INF))>0)            flow+=f;    }}bool dfs1(int u,int obj)//用来找代替增广路径的dfs{    vis[u]=true;    for(int v=0;v<V;++v)        if(G[u][v]>0&&!vis[v])        {            if(v==obj)                return true;            if(dfs1(v,obj))                return true;        }    return false;}bool dfs2(int u,int obj)//用来倒流的dfs{    vis[u]=true;    for(int v=0;v<V;++v)    {        if(lG[u][v]>0&&!vis[v])        {            if(v==obj)                return true;            if(dfs2(v,obj))            {                G[u][v]-=1;                G[v][u]+=1;                lG[u][v]-=1;                lG[v][u]+=1;                return true;            }        }    }    return false;}int main(){    while(~scanf("%d%d%d",&N,&S,&T))    {        --S;        --T;        V=N*2;        init();        //0 ~ N-1 入点        //N ~ 2*N 出点//        cout<<" S: "<<S<<" T: "<<T<<endl;        bool ok=false;        for(int i=0;i<N;++i)            add_edge(i,i+N,1);        for(int i=0;i<N;++i)        {            for(int j=0;j<N;++j)            {                int tmp;                scanf("%d",&tmp);                if(i!=j&&tmp)                {                    add_edge(i+N,j,INF);                    if(i==S&&j==T)                        ok=true;                }            }        }        S+=N;        if(ok)//直接相连,无解        {            puts("NO ANSWER!");            continue;        }        printf("%d\n",dinic(S,T));//最小割        bool first=true;        for(int i=0;i<N;++i)//枚举删除的位置        {            if(G[i][i+N]==1)                continue;            mem(vis,0);            lG[i+N][i]=0;            if(!dfs1(i,i+N))//找不到可以用代替的增光路            {                if(first)                    first=false;                else putchar(' ');                printf("%d",i+1);                mem(vis,0);                dfs2(i,S);//边起点到源点,倒流                mem(vis,0);                dfs2(T,i+N);//边终点到汇点,倒流            }            else lG[i+N][i]=1;        }        putchar('\n');    }        return 0;}


0 0