BZOJ-1143&&BZOJ-2718 祭祀river&&畢業旅行 最長反鏈(Floyed傳遞閉包+二分圖匹配)

蛋蛋安利的雙倍經驗題

1143: [CTSC2008]祭祀river
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1901 Solved: 951
[Submit][Status][Discuss]

Description
在遙遠的東方,有一個神祕的民族,自稱Y族。他們世代居住在水面上,奉龍王爲神。每逢重大慶典, Y族都會在水面上舉辦盛大的祭祀活動。我們可以把Y族居住地水系看成一個由岔口和河道組成的網絡。每條河道連接着兩個岔口,並且水在河道內按照一個固定的方向流動。顯然,水系中不會有環流(下圖描述一個環流的例子)。
這裏寫圖片描述
由於人數衆多的原因,Y族的祭祀活動會在多個岔口上同時舉行。出於對龍王的尊重,這些祭祀地點的選擇必須非常慎重。準確地說,Y族人認爲,如果水流可以從一個祭祀點流到另外一個祭祀點,那麼祭祀就會失去它神聖的意義。族長希望在保持祭祀神聖性的基礎上,選擇儘可能多的祭祀的地點。

Input
第一行包含兩個用空格隔開的整數N、M,分別表示岔口和河道的數目,岔口從1到N編號。接下來M行,每行包含兩個用空格隔開的整數u、v,描述一條連接岔口u和岔口v的河道,水流方向爲自u向v。

Output
第一行包含一個整數K,表示最多能選取的祭祀點的個數。

Sample Input
4 4
1 2
3 4
3 2
4 2

Sample Output
2

【樣例說明】
在樣例給出的水系中,不存在一種方法能夠選擇三個或者三個以上的祭祀點。包含兩個祭祀點的測試點的方案有兩種:
選擇岔口1與岔口3(如樣例輸出第二行),選擇岔口1與岔口4。
水流可以從任意岔口流至岔口2。如果在岔口2建立祭祀點,那麼任意其他岔口都不能建立祭祀點
但是在最優的一種祭祀點的選取方案中我們可以建立兩個祭祀點,所以岔口2不能建立祭祀點。對於其他岔口
至少存在一個最優方案選擇該岔口爲祭祀點,所以輸出爲1011。

HINT
對於每個測試點:如果你僅輸出了正確的被選取的祭祀點個數,那麼你將得到該測試點30%的分數;如果你僅輸出了正確的被選取的祭祀點個數與一個可行的方案,那麼你將得到該測試點60%的分數;如果你的輸出完全正確,那麼你將得到該測試點100%的分數

【數據規模】 N ≤ 100 M ≤ 1 000

Source


2718: [Violet 4]畢業旅行
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 376 Solved: 209
[Submit][Status][Discuss]

Description
這裏寫圖片描述
Input
這裏寫圖片描述
Output
最多可選多少景點

Sample Input
7 6
1 2
2 3
5 4
4 3
3 6
6 7

Sample Output
2

HINT
這裏寫圖片描述
Source
Ctsc2008 River & ural 1533. Fat Hobbits

題意:
給出一個有向無環圖。在其中找出一個最大的點集使得點集中任意兩個點之間不可達。
思路:
最長反鏈;那麼何爲反鏈? 鏈是一個點的集合,這個集合中任意兩個元素v、u,要麼v能走到u,要麼u能走到v。 反鏈是一個點的集合,這個集合中任意兩點誰也不能走到誰。
求法:
Dilworth定理:最長反鏈長度 = 最小鏈覆蓋數
對於最小鏈覆蓋數,可以利用二分圖匹配來求;又應用了一個結論:最小鏈覆蓋數=總點數-最大匹配數
實現:
Floyed傳遞閉包,處理連通性問題; 匈牙利算法硬上,得結果。

證明:(Dilworth定理)
首先,最長反鏈中的每個點,一定在不同的鏈中,所以得到:最長反鏈長度 ≤ 最小鏈覆蓋數
下面關心的是,最長反鏈長度 ≥ 最小鏈覆蓋數嗎?
用數學歸納法。
首先空的圖顯然成立。
假設對於所有點數小於圖G的圖都成立,現在來證G也滿足條件。
兩個定義:源:沒有一個點能走到它的點。(入度爲0) 匯:不能走到任何一個點的點。(出度爲0)
設:
這個圖點集爲V
這個圖的最長反鏈爲A
能走到A中某個點的點的集合爲B,A中某個點能走到的點構成的集合爲C。
(1)若A中至少有一個點既不是源也不是匯
則:
1. B ≠ V (否則反鏈的點全是匯)
2. C ≠ V (否則反鏈的點全是源)
3. B ∪ C = V:反證法,假設有一個點v既不屬於B也不屬於C,那麼說明反鏈上任何一點不能到v,v也不能到反鏈上任何一個點,反鏈加上v之後能成爲更長的反鏈,與假設矛盾。
4. B ∩ C = A:反證法,假設有一個點v既屬於B也屬於C但不屬於A,那麼說明反鏈上某點a能到v,v能到反鏈上某點b。若a = b,則與圖是有向無環圖矛盾。若a ≠ b,則a能先到v,在從v到達b,與a、b都在反鏈上矛盾。
設B構成的子圖、C構成的子圖的最小鏈覆蓋的鏈的集合分別爲C[B]和C[C]。因爲|B| < |V|,|C| < |V|,由歸納假設,C[B]、C[C]大小分別等於B構成的子圖和C構成的子圖的最長反鏈的大小。
考慮一條鏈c ∈ C[B],則c中必然存在一個在最長反鏈上的點v,不然最小鏈覆蓋會大於最長反鏈,與歸納假設矛盾。因爲B ∩ C = A,所以C[B]中v不能到任何一個點。所以v必爲c的匯。
於是得到:A中的點都是C[B]中某條鏈的匯。
同理:A中的點都是C[C]中某條鏈的源。
又B ∪ C = V,B ∩ C = A,C[B]中某條鏈的匯必爲C[C]中某條鏈的源,於是把C[B]和C[C]拼起來,就得到一個原圖G的一種可行的鏈覆蓋了!
考慮這個鏈覆蓋,發現這個鏈覆蓋大小 = 最長反鏈長度,得到最小鏈覆蓋大小 ≤ 最長反鏈長度。又由前面的最小鏈覆蓋大小 ≥ 最長反鏈長度,所以G中最小鏈覆蓋大小 = 最長反鏈長度
(2)若A是所有源的集合或所有匯的集合
若A是所有源的集合,那麼從A中隨便取一個元素v(這個可以隨便找到吧)
再從V中隨便找一個匯u,使得v可以到u(這個也可以隨便找到吧)(u可以等於v)
然後G中去掉v、u形成圖G’。則G’的最長反鏈大小一定小於等於|A| - 1,不然的就可以在G中找到一條不選v、u的最長反鏈,利用(1)的結論可以證明正確性。
由於歸納假設,則G’中一定可以找到一個數量爲|A| - 1的鏈覆蓋。再加上{v} ∪ {u}這條鏈,則可以在G中找到一個數量爲|A|的鏈覆蓋,所以G的最小鏈覆蓋 ≤ 最長反鏈長度。
又由前面的最小鏈覆蓋大小 ≥ 最長反鏈長度,所以G中最小鏈覆蓋大小 = 最長反鏈長度。
證完了,這就是傳說中的Dilworth定理:最小鏈覆蓋數 = 最長反鏈長度。
其對偶定理:最長鏈長度 = 最小反鏈覆蓋數 就不證了,網上的證明爛大街了。
by vfk

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define maxn 210
#define maxm 1010
int n,m,ans=0;
int road[maxn][maxn],part[maxn],f[maxn];
bool used[maxn];

void floyed()
{
    for (int k=1; k<=n; k++)
        for (int i=1; i<=n; i++)
            for (int j=1; j<=n; j++)            
                if (road[i][k] && road[k][j])
                    road[i][j]=1;
}

bool find(int x)
{
    if (f[x]) return 0;
    f[x]=1;
    for (int i=1; i<=n; i++)
        if (road[x][i])
            {
                if (!used[i] || find(part[i]))  
                    {            
                        used[i]=1;part[i]=x;return 1;
                    }         
            }
    return 0;
}

int main()
{
    n=read(),m=read();
    for (int i=1; i<=m; i++)
        {
            int u=read(),v=read();
            road[u][v]=1;
        }
    floyed();
    for (int i=1; i<=n; i++)
        {
            memset(f,0,sizeof(f));
            if (find(i)) ans++;
        }
    printf("%d\n",n-ans);
    return 0;
}
發佈了164 篇原創文章 · 獲贊 9 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章