Usaco 5.4.5

這道題要求刪去最少的點,使得網絡中給定兩點不連通。

這個最少的點在圖論中有具體的定義,有名稱爲點連通度。

那麼,如何求這個點連通度呢?

我們可以考慮從前面的知識進行轉化,前面有一道題目是讓我們求邊連通度。

那麼從邊連通度轉化爲點連通度是否可行呢?答案是可行的。

可以把在網絡中的每個點p(這裏p代表其中一個點),拆成兩個點,p1,p2,在p1,p2之間連一條邊,其容量爲1.

那麼對於原來就在網絡中相連的兩點(又因爲該題爲無向圖),假設這兩點爲p,q,拆點後形成p1,p2,q1,q2,

我們可以在p2和q1之間連一條容量爲INF(maxint),在q2和p1之間也連一條容量爲INF的邊。

做一遍最大流,就可以得出點連通度。


另外,題目還要求我們輸出字典序最小的組成大小爲點連通度的點割集。

這其實也可以借鑑前面那一道求邊連通度的題目。

邊連通度的題目,我們嘗試按升序去刪除每一條邊,如果改變刪除後,網絡中的最大流減小,說名改邊存在於邊割集中。

同理,因爲這道題目我們拆點了,並且拆點後的兩個子點之間有邊相連,所以我們可以嘗試按升序去刪除每一條拆點拆出來的邊,

如果做最大流後流量減少了1(因爲拆點出來的邊容量爲1),那麼該點便屬於點割集,一直做到最大流爲0爲止。


洋洋灑灑寫了這麼多,只是希望大家能懂。微笑

/*
ID: volz.kz.g
PROB: telecow
LANG: C++
*/
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#define INF 0x7FFFFFFF
using namespace std;
ifstream fin("telecow.in");
ofstream fout("telecow.out");

int n,m,s,t,ans;
struct edge_node{
    int v,c,next;
}e[6000];
int edge_num;
int d[6000],c[6000],v[6000];
int g[501][501];
bool cut[501];

void init(){
    memset(e,0,sizeof(e));
    memset(v,0,sizeof(v));
    edge_num=0;
}
void insert(int a,int b,int c){
    //fout << a << " " << b << " " << c << endl;
    ++edge_num;
    e[edge_num].v=b;e[edge_num].c=c;e[edge_num].next=v[a];v[a]=edge_num;
    ++edge_num;
    e[edge_num].v=a;e[edge_num].c=0;e[edge_num].next=v[b];v[b]=edge_num;
}
void makegraph(){
    init();
    for (int i=1;i<=n;++i)
	for (int j=1;j<=n;++j)
	    if (g[i][j]){
		insert(i+n,j,INF);
		insert(j+n,i,INF);
	    }
    for (int i=1;i<=n;++i)
	if (cut[i])
	    insert(i,i+n,1);
	else 
	    insert(i,i+n,0);
}
int opp(int x){
    return (x%2==1)?x+1:x-1;
}
int maxflow(int x,int lim){
    int ret,min_num,max_flow,i,j;
    if (x==t) return lim;
    min_num=2*n;max_flow=0;
    j=v[x];
    while (j!=0){
	if (e[j].c>0){
	    if (d[e[j].v]+1==d[x]){
		ret=min(lim,e[j].c);
		ret=maxflow(e[j].v,ret);
		e[j].c-=ret;e[opp(j)].c+=ret;
		lim-=ret;max_flow+=ret;
		if (lim==0 || d[s]>=2*n) return max_flow;
	    }
	    min_num=min(min_num,d[e[j].v]+1);
	}
	j=e[j].next;
    }
    if (min_num==d[x]) return max_flow;
    --c[d[x]];
    if (c[d[x]]==0) d[s]=2*n;
    d[x]=min_num;c[d[x]]++;
    return max_flow;
}
int main(){
    memset(g,false,sizeof(g));
    memset(cut,true,sizeof(cut));
    fin >> n >> m >> s >>t;
    for (int i=1;i<=m;++i){
	int a,b;fin >> a >> b;
	g[a][b]=true;g[b][a]=true;
    }
    makegraph();
    c[0]=2*n;
    while (d[s]<2*n) ans+=maxflow(s+n,INF);
    fout << ans << endl;//輸出點連通度
    int print[201],print_num=0;
    for (int i=1;i<=n;++i){
	if (i!=s && i!=t){
	    cut[i]=false;
	    makegraph();memset(c,0,sizeof(c));memset(d,0,sizeof(d));
	    int tmp=0;c[0]=2*n;
	    while (d[s]<2*n) tmp+=maxflow(s+n,INF);
	    if (tmp==ans-1){
		ans--;
		print[++print_num]=i;
	    }
	    else{
		cut[i]=true;
	    }
	    if (!ans) break;
	}
    }
    for (int i=1;i<print_num;++i) fout << print[i] << " ";fout << print[print_num] << endl;
    return 0;
}


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