CF1147F Zigzag Game & 穩定婚姻問題學習筆記


CF1147F Zigzag Game

這題太神仙了,不得不記錄一下。

我網絡流做不動了,DS做不動了,DP做不動了,特別自閉。於是博弈論之神(就是隨手切3500博弈的那種) \(\color{black}{\texttt{F}}\color{red}{\texttt{orever_Pursuit}}\) 建議我去學博弈論。

當時看到這題只有兩個標簽:博弈,交互。就去做了。看到是和交互庫玩游戲,感覺很好玩。發現不會,就問了FP。他也不會。然后看題解,發現根本不是博弈(惱。但是這個知識比較重要,還是記一下。准確來說,這是60多年前的論文題/fad,沒做過很難現場搞出來。


題解

可以證明選Bob一定必勝(即選擇先放棋子)。

首先,我們可以默認我們拿到的操作符是 I ,並且我們選的是左邊的數。如果恰好一個有變化,直接把所有邊權取相反數即可。

如果必勝,那么意味着我們選了邊 \(<a,b>\) ,對方選了 \(<b,c>\) ,我們一定可以找到 \(<c,d>\) ,滿足 \(w_{a,b}>w_{b,c},w_{c,d}>w_{b,c}\)

這就是穩定婚姻問題,找到一組可行的匹配即可,直接寫就完事了簡直生草,國外還有論文題。

更明顯一點:如果 \(b\) 認為 \(c\)\(a\) 優,那么 \(c\) 一定不能認為 \(b\)\(d\) 優,否則不穩定。

注意兩邊點的“更優”定義是不同的。左邊的點認為更大的更優,右邊的點認為更小的更優。

這題沒了。可是蒟蒻不會穩定婚姻問題,於是學習筆記就順帶寫了(


穩定婚姻問題學習筆記

男孩女孩太不習慣了 ,我們就用這道題的題面,左邊的點匹配右邊的點

問題定義

給一張完全二分圖(你硬說男生在左女生在右也行),每一對左邊的點與右邊的點之間都有一個評分 \(w_{i,j}\),要求把點兩兩匹配,滿足:

設點 \(a,c\) 在同一邊,點 \(b,d\) 在另一邊,當前 \((a,b),(c,d)\) 匹配。那么對於所有這種 \((a,b,c,d)\) 不能存在 \(w_{b,a}<w_{b,c}\)\(b\) 認為\(c\)\(a\) 優)且 \(w_{c,b}>w_{c,d}\)\(c\) 認為\(b\)\(d\) 優)的情況。

如果存在,那么顯然 \(b,c\) 會組成一對,因為這對於 \(b,c\) 來說都更優,那么他們會各自拒絕自己原來的匹配,就不穩定了。

算法流程

定義數組 \(mch_i\) 表示 \(i\) 的匹配。

  • 每次取出一個左邊的沒有匹配的點。
    • 如果他當前最優的匹配 \(v\) 沒有匹配,那么 \(u\) 匹配 \(v\)
    • 如果 \(v\) 有匹配並且認為 \(u\) 比他當前的匹配更優,那么 \(u,v\) 匹配,原來的 \(mch_v\) 匹配取消,即 \(mch_v\)\(v\) 拒絕了。
  • 如果每一個節點都有匹配,那么婚姻穩定,退出。

注意 最優匹配 指的是還沒有拒絕 \(u\) 的點中,\(u\) 認為最優的匹配。

正確性證明

可行性

假設算法結束后,有一個左邊的點和右邊的點沒有匹配上(存在沒匹配那么左右至少各一個點點匹配)

  • 如果一個右邊的點被嘗試匹配過了,必然有匹配。
  • 如果左邊的點一直沒有匹配,那么會嘗試匹配右邊所有的點。

所以右邊必然匹配滿,那么就不存在失配的問題。

時間復雜度

可以發現,每一個左邊的點至多嘗試 \(n\) 次與右邊的點匹配,那么上界 \(O(n^2)\)

性質

可以發現主動方可以選擇到他可以匹配到的最優的匹配。


\(\mathcal{Code}\) (穩定婚姻部分有詳細注釋。主函數如果你認真看了上面的講解應該能寫出來)

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mkp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define sz(v) (int)v.size()
typedef long long LL;
typedef double db;
template<class T>bool ckmax(T&x,T y){return x<y?x=y,1:0;}
template<class T>bool ckmin(T&x,T y){return x>y?x=y,1:0;}
#define rep(i,x,y) for(int i=x,i##end=y;i<=i##end;++i)
#define per(i,x,y) for(int i=x,i##end=y;i>=i##end;--i)
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return f?x:-x;
}
const int N=55;
int n,w[N][N],mch[N<<1],kth[N<<1],rk[N<<1][N<<1],ID;
vector<int>e[N<<1];
bool cmp1(const int&a,const int&b){return w[ID][a-n]>w[ID][b-n];}
bool cmp2(const int&a,const int&b){return w[a][ID-n]<w[b][ID-n];}
void init(){//穩定婚姻問題模板
	fill(mch+1,mch+n*2+1,0),fill(kth+1,kth+n*2+1,0);
	queue<int>q;
	rep(i,1,n){
		e[i].resize(n+1),e[i+n].resize(n+1);//每一個點的匹配邊大小是 n
		rep(j,1,n)e[i][j]=j+n,e[i+n][j]=j;//拉邊
		ID=i,sort(e[i].begin()+1,e[i].begin()+n+1,cmp1);
		ID=i+n,sort(e[i+n].begin()+1,e[i+n].begin()+n+1,cmp2);//兩邊的點對於“優”的定義不同
		rep(j,1,n)rk[i][e[i][j]]=j,rk[i+n][e[i+n][j]]=j;//處理對於 i 這個節點的所有可能的匹配 j 在 i 看來的優秀程度
		q.push(i);//沒有匹配的點丟進隊列
	}
	while(!q.empty()){
		int u=q.front();q.pop();//找到一個沒有匹配的點
		int v=e[u][++kth[u]];//kth 是 u 當前最優的匹配排名
		if(!mch[v])mch[u]=v,mch[v]=u;//v 還沒有匹配
		else if(rk[v][u]<rk[v][mch[v]]){
			if(mch[v]<=n)q.push(mch[v]);//mch[v] 失去匹配了,重新丟進隊列找匹配
			mch[mch[v]]=0,mch[v]=u,mch[u]=v;//v 認為 u 更優
		}else q.push(u);//沒匹配上,丟進去接着找
	}
}
void Main(){
	scanf("%d",&n);
	rep(i,1,n)rep(j,1,n)w[i][j]=read();
	printf("B\n"),fflush(stdout);
	char fyyAK[5];int x;
	scanf("%s%d",fyyAK,&x);
	if((fyyAK[0]=='I')^(x>n))rep(i,1,n)rep(j,1,n)w[i][j]*=-1;
	init();
	while("fyy txdy"){//只能膜拜!
		printf("%d\n",mch[x]),fflush(stdout);
		scanf("%d",&x);if(!~x)break;
	}
}
signed main(){for(int T=read();T;--T)Main();}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM