CF1147F Zigzag Game & 稳定婚姻问题学习笔记

CF1147F Zigzag Game

这题太神仙了,不得不记录一下。

我网络流做不动了,DS做不动了,DP做不动了,特别自闭。于是博弈论之神(就是随手切3500博弈的那种) (color{black}{ exttt{F}}color{red}{ exttt{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
"),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
",mch[x]),fflush(stdout);
		scanf("%d",&x);if(!~x)break;
	}
}
signed main(){for(int T=read();T;--T)Main();}
原文地址:https://www.cnblogs.com/zzctommy/p/14044374.html