[学习笔记] 邓老师调整法


本篇博客和邓老师论文的区别就是不严谨有代码。

简介

组合优化问题有如下形式:一个问题有一些合法解和不合法解,每个合法解有一个对应的权值,你需要在所有合法解中找出权值最大的一个。

一种显然的做法是:先任取一个合法解,然后对合法解进行微调使得权值变大,一直操作直到无法进行。这一算法看似简单,但在许多问题中有出色的表现。

值得注意的是,这种调整方法得到的答案是不降的,它并不像模拟退火一样有一定概率接受劣解,所以调整方式不能让当前解被局限(能通过答案不降的路径调整到最优解\(/\)较优解)

下面将结合不同的题目类型讲解邓老师调整法的应用,着重体会其思想。

一般图最大匹配

点此看题

我们考虑一个未匹配的点集 \(V\),每次我们从中随机出一个点 \(u\),然后考虑 \(u\) 的邻接点中是否存在未配对的。如果存在那么随机一个直接匹配,答案\(+1\);否则我们随机一个已匹配的点 \(v\),断开 \(v\) 和其匹配点的边,然后把 \((u,v)\) 作为新的匹配加入。

一直重复上述步骤到超时为止,很可惜的是无法通过 \(\tt extra\ test\)

#include 
#include 
#include 
#include  
#include 
using namespace std;
const int M = 505; 
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,ans,mat[M],s[M];vector g[M];
int random(int x)
{
	return (1ll*rand()*rand()+rand())%x;
}
void zxy()
{
	k=0;
	for(int i=1;i<=n;i++)
		if(!mat[i] && !g[i].empty()) s[++k]=i;
	if(!k) return ;//perfect!
	int u=s[random(k)+1];k=0;
	for(int v:g[u]) if(!mat[v])
		s[++k]=v;
	if(k)//random a match node
	{
		int v=s[random(k)+1];
		mat[u]=v;mat[v]=u;return ;
	}
	//adjust it
	for(int v:g[u]) s[++k]=v;
	int v=s[random(k)+1];
	mat[mat[v]]=0;
	mat[u]=v;mat[v]=u;
}
signed main()
{
	n=read();m=read();srand(time(0));
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		g[u].push_back(v);
		g[v].push_back(u);
	}
	while(1.0*clock()/CLOCKS_PER_SEC<=0.95) zxy();
	for(int i=1;i<=n;i++)
		if(mat[i]) ans++;
	printf("%d\n",ans/2);
	for(int i=1;i<=n;i++)
		printf("%d ",mat[i]);
}

相关