其實這個東西比較好玩……
意義比較深遠……
尤其是對廣大屌絲和窮搓矮和魔法師而言……
這個問題在組合數學第9章出現
問的是:
n男n女,每個男的對每個女的有一個評分,每個女的對每個男的有一個評分,然後……
他們兩兩配對……
當然結婚以後有可能不幸福,所以希望找到一種配對方案,滿足
不存在這樣的一對(狗)男女,他們不在一起,同時對對方的評分比對自己配偶高……如果這樣他們就會私奔啊私奔~~~~
這個很明顯是二分圖,但是不是單純的二分圖……
有一個Gale-Shapley算法(應該是叫這兩個名字的兩個人提出的),又稱延遲認可算法
有兩種做法,一種男性最優,一種女性最優
以男性最優爲例:
①每一個男生向自己評分最高且沒拒絕過自己的女性求婚(有可能有女性獲得多個人的求婚)
②如果有女性獲得多個人的求婚(即使已經訂婚),那麼挑選自己評分最高的,與其訂婚,拒絕其他人
注意在②中,如果有女性已經訂婚,那麼她會比較訂婚的人和向自己提出請求的人,選擇評分高的那一個(這就是爲什麼是“訂婚”而非結婚)
算法的正確性隨便google一下就有
我想說的是爲什麼這個方案男性最優……一開始我也沒太弄清
這與我們平常的觀念不符——如果一個女生很受歡迎,那麼她更容易挑到自己滿意的人
但是有一點,有可能女生心儀的男生並不喜歡她,因此沒能向她求婚
舉個例子,有可能n個男生互相不是情敵,那麼第一輪求婚以後,算法就結束了,此時每個男生都心滿意足,而女生不一定……
然後接下來每次男生求婚都是挑的他覺得最好而且自己有機會的,而女生等啊等有可能都等不到想要的那個人……
而且需要注意的是穩定婚姻問題一定有解(就像屌絲最終會找到一個黑木耳……)
這給我們一個啓示……手快有手慢無,一定要主動啊主動~~~~
特別說明以上對基佬不適用……(說不定也適用??)
題目有POJ3487,ZOJ1576,NOJ1710(南開大學OJ……第一次聽說)
題目都差不多,都是裸的,輸入的處理可能麻煩點(ZOJMS最麻煩,得用map……雖然實際上也比較好寫)
POJ3487code:
//其實在處理被拒絕的男生的時候可以開隊列,應該可以加快速度,但是事後纔想起,就算了……反正數據範圍小……
//在處理字符這一點上自認爲寫的很醜……暴醜……其實可以把數組開大,就可以不用減'A'了,當時有點腦殘……
//Lib
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<queue>
using namespace std;
//Macro
#define rep(i,a,b) for(int i=a,tt=b;i<=tt;++i)
#define drep(i,a,b) for(int i=a,tt=b;i>=tt;--i)
#define erep(i,e,x) for(int i=x;i;i=e[i].next)
#define irep(i,x) for(__typedef(x.begin()) i=x.begin();i!=x.end();i++)
#define read() (strtol(ipos,&ipos,10))
#define sqr(x) ((x)*(x))
#define pb push_back
#define PS system("pause");
typedef long long ll;
typedef pair<int,int> pii;
const int oo=~0U>>1;
const double inf=1e20;
const double eps=1e-6;
string name="",in=".in",out=".out";
//Var
class P
{
public:
bool exist;int married;
int pri[60];
}person[60];
int n,pos[60],T;
void Init()
{
memset(person,0,sizeof person);
scanf("%d\n",&n);char ch,s[30];
rep(i,1,n<<1)
scanf("%c%*c",&ch),person[ch-'A'+1].exist=true;
scanf("\n");
rep(i,1,n)
{
scanf("%s",s+1);
rep(i,3,strlen(s+1))
person[s[1]-'A'+1].pri[i-2]=s[i]-'A'+1;
pos[s[1]-'A'+1]=1;
}
rep(i,1,n)
{
scanf("%s",s+1);
rep(i,3,strlen(s+1))
person[s[1]-'A'+1].pri[s[i]-'A'+1]=i-2;
person[s[1]-'A'+1].pri[0]=oo;
}
}
void Set(int first,int second,int third)
{
person[first].married=second;
person[second].married=first;
person[third].married=0;
}
void Work()
{
int cnt=n;
while(cnt)
{
rep(i,'a'-'A'+1,'z'-'A'+1)
{
if(person[i].exist&&!person[i].married)
{
int t=person[i].pri[pos[i]++];
if(person[t].pri[i]<person[t].pri[person[t].married])
{
if(!person[t].married)cnt--;
Set(t,i,person[t].married);
}
}
}
}
rep(i,'a','z')
{
if(person[i-'A'+1].exist)
printf("%c %c\n",i,person[i-'A'+1].married+'A'-1);
}
}
int main()
{
// freopen((name+in).c_str(),"r",stdin);
// freopen((name+out).c_str(),"w",stdout);
for(scanf("%d",&T);T;T--)
{
Init();
Work();
puts("");
}
return 0;
}