題目鏈接:
http://poj.openjudge.cn/practice/C15I/
題目大意:
n對數據,每對兩個字符串str1和str2,代表str1擁有str2的思想。你可以交換兩個人的思想,但是每一輪一個人只能被操作一次。你需要通過多輪操作使得每個人擁有自己的思想。問最少要多少輪,每輪多少對被交換,以及交換哪兩個人。數據保證通過交換能讓每個人的思想回到自己。
思路:由於一定能交換回去,所以思想的所屬關係一定組成若干個圈。被馬神提醒才知道是離散問題。不管是什麼樣的,最多交換兩次就可以。詳細見程序。
#include <stdio.h>
#include <iostream>
#include <string>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <map>
#include <queue>
#include <stack>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=1005;
typedef long long LL;
map <string,int> m;
int rou[MAXN][MAXN],nr=0,ir[MAXN],nmap=0,n,mp[MAXN],vis[MAXN];
string fstr[MAXN];
int findn(string str) //找到字符串序號
{
if(m.count(str))
return m[str];
m[str]=++nmap;
//cout<<nmap<<endl;
fstr[nmap]=str;
return nmap;
}
int main()
{
string str,a,b;
cin>>n;
int flag=1;
memset(vis,0,sizeof(vis)); //是否已經加入某個圈
memset(mp,0,sizeof(mp)); //每個人拿的是誰的思想
memset(ir,0,sizeof(ir)); //每個圈有多少個人
for(int i=0;i<n;i++){
cin>>a>>b;
if(a==b)
continue;
int x=findn(a);
mp[findn(a)]=findn(b);
}
n=m.size();
for(int i=1;i<=n;i++){
if(vis[i])
continue;
int temp=i;
nr++;
while(!ir[nr]||temp!=i){ //建圈
vis[temp]=1;
rou[nr][++ir[nr]]=temp;
temp=mp[temp];
}
}
for(int i=1;i<=nr;i++){ //如果每個圈都只有兩個元素,就只需要一輪,否則兩輪
if(ir[i]>2){
flag=2;
break;
}
}
if(nr==0)
cout<<0<<endl;
else if(flag==1){
cout<<1<<endl;
cout<<nr<<endl;
for(int i=1;i<=nr;i++){
cout<<fstr[rou[i][1]]<<' '<<fstr[rou[i][2]]<<endl; //fstr用於通過序號找到字符串
}
}
else{
cout<<2<<endl;
int num1=nr,num2=0; //第一輪是每個圈1,2換,後面的3和最後一個換,這用往中間對稱
for(int i=1;i<=nr;i++){ //第二輪是2和最後一個這樣往中間對稱換
num1+=(ir[i]-2)/2;
num2+=(ir[i]-1)/2;
}
cout<<num1<<endl;
for(int i=1;i<=nr;i++){
cout<<fstr[rou[i][1]]<<' '<<fstr[rou[i][2]]<<endl;
// swap(rou[i][1],rou[i][2]);
int l=3,r=ir[i];
while(l<r){
cout<<fstr[rou[i][l++]]<<' '<<fstr[rou[i][r--]]<<endl;
// swap(rou[i][l++],rou[i][r--]);
}
}
cout<<num2<<endl;
for(int i=1;i<=nr;i++){
if(ir[i]>2)
cout<<fstr[rou[i][1]]<<' '<<fstr[rou[i][3]]<<endl;
int l=4,r=ir[i];
while(l<r){
cout<<fstr[rou[i][l++]]<<' '<<fstr[rou[i][r--]]<<endl;
}
}
}
return 0;
}