[Luogu 1963] NOI2009 变换序列

[Luogu 1963] NOI2009 变换序列

<题目链接>


先%Dalao's Blog

什么?二分图匹配?这个确定可以建图?

「没有建不成图的图论题,只有你想不出的建模方法。」

建图相当玄学,不过理解大约也那么奇怪。

题里面对D(x,y)的定义那一长句,一开始没看明白,但实际会发现是一个环,而对于每一个点u,符合给定距离的点都有且只有2个(v1 && v2),连u->v1 && u->v2。

对于链式前向星选手,连边的时候注意先连终点序号大的,这样才能保证遍历时从小到大。

为什么要做这个操作呢?因为要求输出字典序最小的解,就必须保证较小的点优先匹配较小的点。

匈牙利算法的过程,总是通过调整先前匹配的点,而使当前点尽量不动

所以,匈牙利算法倒序跑,增广时优先选小的点,这样就能够保证「越小的点越能匹配较小的点」,从而实现字典序最小化。(贪心思想)

最终输出X部每个点的匹配点即可。

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN=20010,MAXM=20010;
bool vis[MAXN];
int n,cnt,ans,head[MAXN],match[MAXN];
struct edge
{
	int nxt,to;
}e[MAXM];
int Solve(int x)
{
	if(x<0)
		x+=n;
	if(x>=n)
		x-=n;
	return x+n;
}
void AddEdge(int u,int v)
{
	e[++cnt].nxt=head[u],e[cnt].to=v,head[u]=cnt;
}
void AddEdges(int u,int x)
{
	int v1=Solve(u+x),v2=Solve(u-x);
	if(v1<v2)
		swap(v1,v2);
	AddEdge(u,v1),AddEdge(u,v2);
}
bool DFS(int u)
{
	for(int i=head[u],v;i;i=e[i].nxt)
		if(!vis[v=e[i].to])
		{
			vis[v]=1;
			if(!match[v] || DFS(match[v]))
			{
				match[u]=v,match[v]=u;
				return 1;
			}
		}
	return 0;
}
bool Hungary(void)
{
	for(int i=n-1;i>=0;--i)
		if(!match[i])
		{
			memset(vis,0,sizeof vis);
			ans+=DFS(i);
		}
	return n==ans;
}
int main(int argc,char *argv[])
{
	scanf("%d",&n);
	for(int i=0,x;i<n;++i)
	{
		scanf("%d",&x);
		AddEdges(i,x);
	}
	if(Hungary())
		for(int i=0;i<n;++i)
			printf("%d ",match[i]-n);
	else
		printf("No Answer");
	putchar('\n');
	return 0;
}

谢谢阅读

posted @ 2018-01-11 21:54  Capella  阅读(169)  评论(0编辑  收藏  举报

谢谢光临