1. 程式人生 > >信與信封問題

信與信封問題

Description

【問題描述】:   John先生晚上寫了n封信,並相應地寫了n個信封將信裝好,準備寄出。但是,第二天John的兒子Small John將這n封信都拿出了信封。不幸的是,Small John無法將拿出的信正確地裝回信封中了。 【程式設計任務】:   將Small John所提供的n封信依次編號為1,2,…,n;且n個信封也依次編號為1,2,…,n。假定Small John能提供一組資訊:第i封信肯定不是裝在信封j中。請程式設計幫助Small John,儘可能多地將信正確地裝回信封。

Input

  輸入檔案的第一行是一個整數n(n≤100)。信和信封依次編號為1,2,…,n。   接下來的各行中每行有2個數i和j,表示第i封信肯定不是裝在第j個信封中。檔案最後一行是2個0,表示結束。

Output

  輸出檔案的每行有2個數i和j,表示第i封信肯定是裝在第j個信封中。請按信的編號i從小到大順序輸出。若不能確定正確裝入信封的任何信件,則輸出“none”。

Sample Input

3

1 2

1 3

2 1

0 0

Sample Output

1 1 二分圖簡單建模。i->j表示i可能在j信封中。樸素的想法是求一遍最大匹配後每次暴力刪邊在做最大匹配,如果小於原答案,則肯定在這個信封中,由於n只有100,完全可過。

事實上,理解了二分圖的這個模型之後,我們只需對其查詢一次增廣路即可。

#include<bits/stdc++.h>
using namespace std;
const int Maxn=205;
int g[Maxn][Maxn];
int n,my[Maxn],mat[Maxn];
bool vst[Maxn];
bool findpath(int x){
	for(int i=n+1;i<=n*2;++i)if(g[x][i]&&!vst[i]){
		vst[i]=1;
		if(!my[i]||findpath(my[i])){
			my[i]=x;
			return 1;
		}
	}
	return 0;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;++i)
		for(int j=n+1;j<=n*2;++j)g[i][j]=1;
	int i,j;for(cin>>i>>j;i&&j;cin>>i>>j)g[i][j+n]=0;
	int ret=0,num=0;
	for(int i=1;i<=n;++i){
		for(int j=n+1;j<=n*2;++j)vst[j]=0;
		ret+=findpath(i);
	}
	for(int i=n+1;i<=n*2;++i)mat[i]=my[i];
	for(int x=1;x<=n;++x)
		for(int y=n+1;y<=n*2;++y)if(my[y]==x){
			g[x][y]=0;
			my[y]=0;//記住這裡要斷 
			for(int i=n+1;i<=n*2;++i)vst[i]=0;
			if(!findpath(x))++num,cout<<x<<" "<<y-n<<"\n";
			for(int i=n+1;i<=n*2;++i)my[i]=mat[i];
			g[x][y]=1;
		}
	if(!num)puts("none");
	return 0;
}