稳定婚姻系统 学习笔记
题意
原题是关于男女配对婚姻的,但是由于这个背景敏感,可能会由于表述不够客观而挑起性别对立,于是将题面进行更加合理公正,尊重人权的更改。
现在有 \(n\) 个生理性别为男性,性别认同亦为男性,性倾向为女性的人,以及有 \(n\) 个生理性别为女性,性别认同为女性,性倾向为男性的人
现在有 \(n\) 个红点和 \(n\) 个蓝点(请注意:这里的红点蓝点并非隐喻或暗指向上文的男性和女性),第 \(i\) 个红点匹配第 \(j\) 个蓝点的权值为 \(a(i,j)\)。你需要求出一种匹配方案,将男性和蓝点两两匹配,使得不存在两组匹配 \((p,x),(q,y)\),使得 \(a(p,x)<a(p,y)\) 且 \(a(q,y)<a(q,x)\),(\(p,q\) 是红点,且 \(x,y\) 是蓝点)。
解法
我们采用贪心算法来解决这个与性别无关的问题。
我们枚举每一个红点 \(p\),并按照权值从大到小第枚举它可以匹配的蓝点 \(x\)。如果枚举到的蓝点没有与其它红点形成匹配,那么就让红点与此蓝点匹配即可。
否则,假设此蓝点已经婚姻的红点为 \(q\)。那么比较 \(a(p,x),a(q,y)\),如果 \(a(p,x)>a(q,y)\) 那么就让此蓝点匹配上 \(p\),而让 \(q\) 按照上述方法重新寻找匹配即可。
容易发现,此算法复杂度是 \(O(n^2)\) 的。
例题
https://lightoj.com/problem/employment
本题就将性别问题非常合理地转化成了公司雇人问题。代码如下,虽然是 Wreng Answer
:
#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define per(i,x,y) for(int i=x;i>=y;--i)
#define lon long long
using namespace std;
const int n7=204;
int T,n,a[n7][n7],rk[n7][n7],vis[n7],que[n7*n7],head,tail;
int rd(){
int shu=0;bool fu=0;char ch=getchar();
while( !isdigit(ch) ){if(ch=='-')fu=1;ch=getchar();}
while( isdigit(ch) )shu=(shu<<1)+(shu<<3)+ch-'0',ch=getchar();
return fu?-shu:shu;
}
int main(){
T=rd();
rep(TT,1,T){
n=rd();
rep(i,1,n+n)rep(j,1,n)a[i][j]=rd(),rk[i][ a[i][j] ]=j;
memset(vis,0,sizeof vis);
head=1,tail=n;
rep(i,1,n)que[i]=i;
while(head<=tail){
int o=que[head];head++;
rep(j,1,n){
int yo=a[o][j];
if(!vis[yo]){vis[yo]=o;break;}
else{
int yo=a[o][j],z=vis[yo];
if(rk[yo][z]>rk[yo][o]){vis[yo]=o,tail++,que[tail]=z;}
}
}
}
printf("Case %d:",TT);
rep(i,n+1,n+n){
printf("(%d %d)",vis[i],i);
if(i<n+n)putchar(' ');
}
if(TT<T)putchar('\n');
}
return 0;
}