【bzoj1143: [CTSC2008]祭祀river】有向無環圖的最長反鏈
1143: [CTSC2008]祭祀river
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3192 Solved: 1632
[Submit][Status][Discuss]
Description
在遙遠的東方,有一個神祕的民族,自稱Y族。他們世代居住在水面上,奉龍王為神。每逢重大慶典, Y族都 會在水面上舉辦盛大的祭祀活動。我們可以把Y族居住地水系看成一個由岔口和河道組成的網路。每條河道連線著 兩個岔口,並且水在河道內按照一個固定的方向流動。顯然,水系中不會有環流(下圖描述一個環流的例子)。由於人數眾多的原因,Y族的祭祀活動會在多個岔口上同時舉行。出於對龍王的尊重,這些祭祀地點的選擇必 須非常慎重。準確地說,Y族人認為,如果水流可以從一個祭祀點流到另外一個祭祀點,那麼祭祀就會失去它神聖 的意義。族長希望在保持祭祀神聖性的基礎上,選擇儘可能多的祭祀的地點。
Input
Output
第一行包含一個整數K,表示最多能選取的祭祀點的個數。
Sample Input
4 41 2
3 4
3 2
4 2
Sample Output
2【樣例說明】
在樣例給出的水系中,不存在一種方法能夠選擇三個或者三個以上的祭祀點。包含兩個祭祀點的測試點的方案有兩種:
選擇岔口1與岔口3(如樣例輸出第二行),選擇岔口1與岔口4。
水流可以從任意岔口流至岔口2。如果在岔口2建立祭祀點,那麼任意其他岔口都不能建立祭祀點
但是在最優的一種祭祀點的選取方案中我們可以建立兩個祭祀點,所以岔口2不能建立祭祀點。對於其他岔口
至少存在一個最優方案選擇該岔口為祭祀點,所以輸出為1011。
icpc上居然出了這個原題,就爆炸了,回來補一補二分圖和有向無環圖的東西。
1、二分圖最大匹配
匈牙利演算法O(V*E)的複雜度內可求出二分圖的最大匹配
2、二分圖最小點覆蓋
找到點數最少的一個點集,使得二分圖裡的所有邊至少有一個端點是在該點集中
最小點覆蓋=最大匹配
3、二分圖最小邊覆蓋
找到邊數最少的一個邊集,使得二分圖裡的所有點至少是一條邊的端點
最小邊覆蓋=圖中點的個數-最大匹配
證明:把最大匹配的邊都加入,則剩下的點之間都沒有邊,那麼再把剩下的點都連線起來,邊數=最大匹配+圖中點的個數-2*最大匹配=圖中點的個數-最大匹配
4、二分圖最大獨立集
找到一個點數最大的點集,使得點集裡的所有點對之間在原圖裡沒有邊。
最大獨立集=圖中點的個數-最大匹配
證明:把所有點加入點集中,最大獨立集問題就轉化為刪去最少的點使得最終的點集是個獨立集,那麼 最大獨立集=圖中點的個數-最小點覆蓋=圖中點的個數-最大匹配
最小不相交路徑覆蓋:每一條路徑經過的頂點各不相同。如,最小路徑覆蓋數為3:1->3>4,2,5。
最小可相交路徑覆蓋:每一條路徑經過的頂點可以相同。如,最小路徑覆蓋數為2,1->3->4,2->3>5。
5、有向無環圖最小不相交路徑覆蓋
用最少的不相交路徑覆蓋所有頂點。
將有向無環圖中的各點拆點,i點拆為ai,bi ,對於一條邊:i->j,轉化為 ai->bj, 便可把有向無環圖轉化為二分圖。有向無環圖最小不相交路徑覆蓋=圖中點的個數-最大匹配
證明:剛開始每個點為一條路徑,則開始時有n條路徑,對於二分圖中的每一條匹配邊就代表把兩條路徑變為一條路徑,則最後的答案就是圖中點的個數-最大匹配
6、有向無環圖最小可相交路徑覆蓋
用最少的可相交路徑覆蓋所有頂點。
處理出有向無環圖中所有點的到達情況,拆點,若i點可經過一條路徑到達j點,那麼連一條ai->bj 的邊,此時有向圖邊轉化為二分圖,問題也就轉化為5、有向無環圖最小不相交路徑覆蓋。
在有向無環圖中,有如下的一些定義和性質:
鏈:一條鏈是一些點的集合,鏈上任意兩個點x, y,滿足要麼 x 能到達 y ,要麼 y 能到達 x 。
反鏈:一條反鏈是一些點的集合,鏈上任意兩個點x, y,滿足 x 不能到達 y,且 y 也不能到達 x。
一個定理:最長反鏈長度 = 最小鏈覆蓋(用最少的鏈覆蓋所有頂點)
對偶定理:最長鏈長度 = 最小反鏈覆蓋 ///????這個是什麼意思沒看懂,有人看懂了評論下
最小鏈覆蓋也就是最小可相交路徑覆蓋。
經過上面一些亂七八糟的介紹,可以知道,這道題目就是求最長反鏈長度,就是求最長反鏈長度,就是求有向無環圖最小可相交路徑覆蓋,見(6)
求各點的聯通性可以用Floyd 也可以直接搜。
#include<cstdio>
#include<cstring>
#define M 1005
#define N 105
using namespace std;
int k,K,fir[N],Fir[N],n,m,l,r,sign[N],link[N],vis[N];
struct he{
int r,nx;
}A[N*N],a[M];
void add(int l,int r){
k++;a[k].r=r;a[k].nx=fir[l];fir[l]=k;
}
void Add(int l,int r){
K++;A[K].r=r;A[K].nx=Fir[l];Fir[l]=K;
}
void dfs(int x,int f){
vis[x]=1;
if(x!=f)Add(f,x);
for(int i=fir[x];i!=-1;i=a[i].nx)
if(!vis[a[i].r]){
dfs(a[i].r,f);
}
}
bool check(int x){
for(int i=Fir[x];i!=-1;i=A[i].nx)
if(!sign[A[i].r]){
sign[A[i].r]=1;
if(link[A[i].r]==0){
link[A[i].r]=x;
return true;
}
if(check(link[A[i].r])){
link[A[i].r]=x;
return true;
}
}
return false;
}
int hungery(){
int ans=n;
for(int i=1;i<=n;i++){
memset(sign,0,sizeof(sign));
if(check(i)) ans--;
}
return ans;
}
int main(){
memset(fir,-1,sizeof(fir));
memset(Fir,-1,sizeof(Fir));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&l,&r);
add(l,r);
}
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
dfs(i,i);
}
printf("%d",hungery());
}