1. 程式人生 > >網路流(最小割最大流(記錄路徑))【POJ1815】

網路流(最小割最大流(記錄路徑))【POJ1815】

  【POJ1815】

出處:原帖

題意:就是求s點到t點,最少去掉幾個點使得他們不連通。如果無解輸出NO ANSWER! 

  解題思路 因為最小割只能求割掉幾條邊的解,我們要求的是割掉幾個點。那麼我們可以這樣考慮:把每個點拆成入點和出點。入點->出點權值為1。那麼割掉這條邊就相當於割掉這個點了,就能把這題轉化成最小割。那麼原來的邊,我們是不希望去割它的,所以我們將原來的邊的權值設定為INF。例如,原來邊為u->v那麼就變成out(u)->in(v)。這樣跑一次最大流,跑出來的答案就是最小割掉的點數。 
       這題還要我們求出一個點集,就是割掉的點,按字典序排列。因為割點可能會有多種組合,所以最簡單的思路就是列舉。如果這個點割掉之後,跑出來的答案能少割一個點,那麼這個點就是要求的點之一,割掉它,繼續列舉,一直到枚舉出所有答案為止。 

       應該還有不列舉的更好方法,求教學。。。 

程式碼:

#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<map>
#include<iostream>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<stack>
#include<cctype>
#include<set>
#include<ctime>
#include<cassert>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF=1e9+7;
typedef pair<int,int> pii;
#define MP make_pair
#define PB push_back
#define in(x) (x)
#define out(x) (x+n)
int n,st,ed;
int start[210][210],pic[555][555],d[555];
bool del[555];
vector<int> pr;
void build(){
    for(int i=1;i<=n;i++){
        if(!del[i])
            pic[in(i)][out(i)]=1;//拆點,後面枚舉了割掉的點,所以用del陣列標記
        //容量是1,希望在割邊的時候割它,割掉點中的邊相當於割點,就把最小割轉化成割點了
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)if(start[i][j]){
            pic[out(i)][in(j)]=INF;
            //割這條邊的流量是INF,意味著不希望去割它
        }
    }
}
bool BFS(){
    queue<int> Q;
    memset(d,-1,sizeof d);
    Q.push(st);d[st]=0;
    while(!Q.empty()){
        int s=Q.front();Q.pop();
        for(int i=1;i<=n+n;i++){
            if(pic[s][i]>0&&d[i]<0){
                d[i]=d[s]+1;if(i==ed)return true;
                Q.push(i);
            }
        }
    }
    return false;
}
int DFS(int s,int t,int flow){
    if(s==t||flow==0)return flow;
    int ans=0;
    for(int i=1;i<=n+n;i++){
        if(d[i]==d[s]+1&&pic[s][i]>0){
            int ff=DFS(i,t,min(pic[s][i],flow));
            if(ff>0){
                pic[s][i]-=ff;
                pic[i][s]+=ff;
                ans+=ff;
                flow-=ff;
                if(!flow)break;
            }
        }
    }
    if(!ans)d[s]=-1;
    return ans;
}
int dinic(){
    int ans=0;
    while(BFS()){
        ans+=DFS(st,ed,INF);
    }
    return ans;
}
int main()
{
//    freopen("D://input.txt","r",stdin);
    scanf("%d%d%d",&n,&st,&ed);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&start[i][j]);
        }
    }
    if(start[st][ed]){printf("NO ANSWER!\n");return 0;}
    //只有直接相連的時候會無解
    st=out(st);ed=in(ed);
    build();
    int ans=dinic();
    printf("%d\n",ans);
    if(!ans)return 0;
    for(int i=1;i<=n;i++){
    //因為割點可能會有很多組合,而且要求字典序最小,所以只能割一個就標記上,一直到割完為止
        if(i==st-n||i==ed)continue;
        del[i]=true;
        build();
        int t=dinic();
        if(t<ans){
            ans--;pr.PB(i);
            if(ans==0)break;
        }
        else del[i]=false;;
    }
    for(int i=0;i<(int)pr.size();i++){
        if(i)printf(" ");
        printf("%d",pr[i]);
    }
    printf("\n");
    return 0;
}