poj friendship 最大流最小割

http://acm.pku.edu.cn/JudgeOnline/problem?id=1815

Friendship

Time Limit: 2000MS

 

Memory Limit: 20000K

Total Submissions: 3621

 

Accepted: 891

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 3

1 1 0

1 1 1

0 1 1

Sample Output

1

2
-problem: 求无向图的最小点割集, 输出集合, 要求字典序最小. 顶点数n<=200, 边数(单向)<=5000.
solution:
拆点, 做一次最大流, 从小到大枚举删点, 再求最大流, 若流减小则结果包含此点.

求无向图和有向图的最小点割集, 方法几乎是一样的, 拆点, 转化成求最小边割集, 用最大流算法求解:

1.       若原图为无向图, 则把每条边看作两条有向边. 设原图为G, 构造新图G’.

2.      G图中的每个顶点v变成G’中的两个顶点v’v", 顶点v’v"有一条弧连接, 弧容量为1.

3.      G图中的每条边e=uv, G’中有两条弧e’= u"v’, e"=v"u’与之对应, 容量均为∞.

4.      s"为源顶点, t’为汇顶点, 求最大流, 最大流的值即为最小点割集的点数.

要注意的是在枚举割点的时候,不能在map矩阵上操作,因为前一次求最大流的过程中就改变了容量,则下次还需要重新建图

 

题目要求字典序输出, 那么就在第一次求出最大流以后从小到大枚举每个点u, G’中删除u’u", 再求最大流f. f小于之前的, 则说明此点必然在最小点割集中, f1; f等于之前的, 则恢复u’u". 枚举过程中如果f=0, 跳出.

#define MAXN 405

#define INF 1000000000

#include<iostream>

using namespace std;

int n,s,t,dis[MAXN][MAXN],map[MAXN][MAXN],dist[MAXN][MAXN];

void build_map()

{

       int i,j;

       memset(dist,0,sizeof(dist));

       memset(map,0,sizeof(map));

       for(i=0;i<n;i++)

       {

              map[i][i+n]=1;

              dist[i][i+n]=1;

       }

       for(i=0;i<n;i++)

              for(j=0;j<n;j++)

                     if(dis[i][j])

                     {

                            dist[i+n][j]=dist[j+n][i]=INF;

                            map[i+n][j]=map[j+n][i]=INF;

                     }

}

int SAP(int map[][MAXN],int v_count,int s,int t)

{  

    int cur_flow,max_flow,cur,min_label,temp,i; 

    char flag;                                    

    int cur_arc[MAXN],label[MAXN],neck[MAXN];       

    int label_count[MAXN],back_up[MAXN],pre[MAXN];   

    memset(label,0,MAXN*sizeof(int));  

    memset(label_count,0,MAXN*sizeof(int));  

    memset(cur_arc,0,MAXN*sizeof(int));  

    label_count[0]=v_count;                         

    neck[s]=s;  

    max_flow=0;  

    cur=s;  

    cur_flow=INF;  

    while(label[s]<v_count)

    {  

        back_up[cur]=cur_flow;  

        flag=0;  

        for(i=cur_arc[cur];i<v_count;i++) 

        {  

                     if(map[cur][i]!=0&&label[cur]==label[i]+1)  

                     {  

                            flag=1;  

                            cur_arc[cur]=i;   

                            if(map[cur][i]<cur_flow) 

                            {  

                                   cur_flow=map[cur][i];  

                                   neck[i]=cur;  

                            }  

                            else 

                            {  

                                   neck[i]=neck[cur];  

                            }  

                            pre[i]=cur; 

                            cur=i;  

                            if(i==t)  

                            {  

                                   max_flow+=cur_flow;  

                                   while(cur!=s)  

                                   {  

                                          if(map[pre[cur]][cur]!=INF)map[pre[cur]][cur]-=cur_flow;  

                                          back_up[cur] -= cur_flow;  

                                          if(map[cur][pre[cur]]!=INF)map[cur][pre[cur]]+=cur_flow;  

                                          cur=pre[cur];  

                                   }  

                                   cur=neck[t];  

                                   cur_flow=back_up[cur];   

                            }  

                            break;  

                     }  

        }  

        if(flag)

                     continue;  

        min_label=v_count-1; 

        for(i=0;i<v_count;i++)  

        {  

            if(map[cur][i]!=0&&label[i]<min_label)  

            {  

                min_label=label[i];  

                temp=i;  

            }  

        }  

        cur_arc[cur]=temp; 

        label_count[label[cur]]--;  

        if(label_count[label[cur]]==0)

                     break;   

        label[cur]=min_label+1; 

        label_count[label[cur]]++;   

        if(cur!=s)  

        {  

                     cur=pre[cur];  

                     cur_flow=back_up[cur];  

        }  

    }  

    return(max_flow);  

}

 

int main()

{

       int i,j,k;

       cin>>n>>s>>t;

       for(i=0;i<n;i++)

              for(j=0;j<n;j++)

                     scanf("%d",&dis[i][j]);

       if(dis[s-1][t-1])

       {

              cout<<"NO ANSWER!"<<endl;

              return 0;

       }

       s--;t--;

       build_map();

       int cut[MAXN],cnt=0;

       int curent_flow=SAP(map,2*n,s+n,t);

       for(i=0;i<n&&curent_flow;i++)

       {

              if(i==s||i==t)

                     continue;

              for(j=0;j<n+n;j++)

                     for(k=j;k<n+n;k++)

                     {

                            map[j][k]=dist[j][k];

                            map[k][j]=dist[k][j];

                     }

              map[i][i+n]=0;

              if(curent_flow>SAP(map,2*n,s+n,t))

              {

                     curent_flow--;

                     cut[cnt++]=i;

                     dist[i][i+n]=0;

              }

       }

       cout<<cnt<<endl;

       if(cnt!=0)

       {

              cout<<cut[0]+1;

              for(i=1;i<cnt;i++)

                     cout<<" "<<cut[i]+1;

              cout<<endl;

       }

       return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值