【noip模擬】最小點覆蓋
Time Limit: 1000ms Memory Limit: 128MB
Description
最小點覆蓋是指在二分圖中,用最小的點集覆蓋所有的邊。當然,一個二分圖的最小點覆蓋可能有很多種。
現在給定一個二分圖,請你把圖中的點分成三個集合:
如果在任何一種最小點覆蓋中都不包含這個點,則認為該點屬於N集合。
如果在任何一種最小點覆蓋中都包含這個點,則認為該點屬於A集合。
如果一個點既不屬於N集合,又不屬於A集合,則認為該點屬於E集合。
Input
第一行包含三個整數n, m, k,分別表示二分圖A側點的數量,二分圖B側點的數量,邊的數量。
接下來k行,每行兩個整數i, j,分別表示二分圖A側第i號點與二分圖B側第j號點有連邊。
數據保證無重邊。
Output
第一行輸出一個長度為n的字符串,其中第i個字符表示二分圖A側第i個點所屬的集合。
第二行輸出一個長度為m的字符串,其中第i個字符表示二分圖B側第i個點所屬的集合。
Sample Input
11 9 22
1 1
1 2
1 3
1 8
1 9
2 1
2 3
3 2
3 4
4 3
4 5
5 2
5 4
5 6
6 6
6 7
7 5
7 7
8 7
9 7
10 7
11 7
Sample Output
AEEEEEENNNN
EEEEEEANN
HINT
對於10%的數據,$1≤n,m≤5$
對於40%的數據,$1≤n,m≤100$
對於100%的數據,$1≤n,m≤1000, 0≤k≤n∗m$
[吐槽]
場上?嗯。。大眼瞪小眼qwq
[題解]
嗯首先碼一下最小點覆蓋的相關知識
http://www.matrix67.com/blog/archives/116 (二分圖最大匹配的König定理及其證明)
(所以說其實我也很想知道為什麽那個o上面有兩個點啊哈哈哈哈)
嗯還是把自己對於上面那篇東西的理解寫一下吧整理整理qwq
這題要按照最小點覆蓋是否包含一個點來分類,所以先想一下怎麽得到最小點覆蓋的一種可行方案
König定理
一個二分圖中最大的匹配數=該圖中最小點覆蓋數
嗯首先還是先把定義搬出來吧
點覆蓋:就是一個點集,滿足該圖的所有邊都有至少一個頂點在這個點集中,點集大小最小的成為最小點覆蓋
證明的話稍微整理一下(learn from matrix67%%%)
假設我們現在已經跑出了一個最大匹配,匹配數為$m$,考慮構造一種點覆蓋的可行方案
一個簡單的想法:
我們按照一種方式給這堆點打上標記,以B部分中所有沒有被匹配到的點為起點,順著匈牙利算法中的交錯軌(也就是匹配邊和非匹配邊交替著走)遍歷直到不能走下去了為止,並給沿路上的點全部打上標記
現在我們將B部分中沒有打上標記的點和A部分中打上標記的點變成一個點集
這個點集就是一種可行的點覆蓋,而且是最小點覆蓋
為啥?
我們先將交錯軌標號所有的一些奇妙性質列出來再進行證明會比較方便一點
對於一條在交錯軌上的邊$(u,v)$,必定滿足:
1.$u,v$均為標號點
2.若該邊為匹配邊,則遍歷時先走到$u$再走到$v$,即從A部分到B部分
3.若改變為非匹配邊,則遍歷時先走到$v$再走到$u$,即從B部分走到A部分
首先證明一下這是一個點覆蓋
我們可以把邊分為兩類,一類是在某條交錯軌上的邊,一類不在
對於在交錯軌上面的邊,顯然我可以通過選取A部分中打上標記也就是遍歷到的點來覆蓋掉
而對於不在交錯軌上且沒有被覆蓋到的邊,它在B部分的端點一定是沒有被標記的
(否則就說明其在A部分的端點一定是沒有被標記的,
而這種邊是不可能存在的,用反證法
設存在一條邊$(u,v)$,$u$沒被標記,$v$被標記,
則$v$應該被交錯軌經過
若這條邊是匹配邊,則一定是由$u$走到$v$,也就是說$v$的標記一定來自$u$,
因此$u$也應該有標記,矛盾
若這條邊是非匹配邊,則到$v$的交錯軌可以繼續走下去
這條邊也應該在交錯軌上,矛盾
綜上,不存在這種邊)
所以這種選法就一定可以保證所有的邊都被覆蓋到啦
第二,證明一下這個點覆蓋的大小$=m$
首先看我們選的用來覆蓋交錯軌上的邊的點
因為我們在標記的時候走的是一段段的交錯軌,並且我們的起點是在右邊
所以左邊的A部分中被標記的點的個數必定與交錯軌中匹配邊的個數相等
接著看剩下的部分
由於交錯軌標號方式,交錯軌上的邊的兩端的點必定都是被標記的點
換句話來說就是非交錯軌上的且沒有被覆蓋到的邊的右端點肯定連到的是未被標記的B部分的點
而每個未被標記的B部分的點必定會連且只會連出一條匹配邊(繼續反證嗯,比較簡單不寫了)
所以數量也是相同的
所以兩個加一下就是$m$啦
最後,證明這是最小點覆蓋
嗯。。一句話搞定:覆蓋$m$條匹配邊最少都要$m$個點,所以當然就是最小的啦
搞掂撈面
回到這道題
所以說前面講了這麽多好像現在才進入正題。。
嗯不管了
然後我們就可以得出一個結論:
如果一個點沒有任何一條匹配邊連到,那麽這個點肯定不在最小點覆蓋中
從而與它相連的點必定要在最小點覆蓋中(不然這兩點之間的連邊就不能覆蓋到了)
而對於一個必定在最小點覆蓋中的點,與它的匹配點必定不在最小點覆蓋中
然後就這樣順著遍歷同時標記就好啦
對於那些沒有被標記到的點,肯定就是屬於E的啦
好的於是在漫長的鋪墊之後這題終於解決啦ovo
[一些細節]
首先。。要用long long
其次。。註意邊數
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int MAXN=1010*2; 6 struct xxx 7 { 8 int y,next; 9 }a[1010*1010*2]; 10 char mark[MAXN]; 11 bool vis[MAXN]; 12 int h[MAXN],match[MAXN]; 13 int n,m,tot,k,ans; 14 int add(int x,int y); 15 bool dfs(int x); 16 int solve(); 17 int work(int x,int op); 18 19 int main() 20 { 21 // freopen("a.in","r",stdin); 22 23 int x,y; 24 scanf("%d%d%d",&n,&m,&k); 25 memset(h,-1,sizeof(h)); 26 tot=0; 27 for (int i=1;i<=k;++i) 28 { 29 scanf("%d%d",&x,&y); 30 add(x,n+y); add(n+y,x); 31 } 32 memset(match,0,sizeof(match)); 33 for (int i=1;i<=n+m;++i) mark[i]=‘E‘; 34 for (int i=1;i<=n;++i) 35 if (!match[i]) 36 { 37 memset(vis,false,sizeof(vis)); 38 if (dfs(i)) ++ans; 39 } 40 // for (int i=1;i<=n;++i) printf("%d ",match[i]-n); 41 solve(); 42 for (int i=1;i<=n;++i) printf("%c",mark[i]); 43 printf("\n"); 44 for (int i=n+1;i<=n+m;++i) printf("%c",mark[i]); 45 } 46 47 int add(int x,int y) 48 { 49 a[++tot].y=y; a[tot].next=h[x]; h[x]=tot; 50 } 51 52 bool dfs(int x) 53 { 54 int u; 55 for (int i=h[x];i!=-1;i=a[i].next) 56 { 57 u=a[i].y; 58 if (vis[u]) continue; 59 vis[u]=true; 60 if (!match[u]||dfs(match[u])) 61 { 62 match[x]=u; 63 match[u]=x; 64 return true; 65 } 66 } 67 return false; 68 } 69 70 int solve() 71 { 72 for (int i=1;i<=n+m;++i) 73 { 74 if (!match[i]) 75 mark[i]=‘N‘,work(i,0); 76 } 77 } 78 79 int work(int x,int op) 80 { 81 int u; 82 if (op==1) 83 { 84 mark[x]=‘A‘; 85 work(match[x],0); 86 return 0; 87 } 88 mark[x]=‘N‘; 89 for (int i=h[x];i!=-1;i=a[i].next) 90 { 91 u=a[i].y; 92 if (mark[u]==‘E‘) work(u,1); 93 } 94 }挫挫滴代碼
【noip模擬】最小點覆蓋