您的位置:首页 > 其它

BZOJ 1562 浅谈匈牙利算法性质挖掘以【变换序列】即最小字典序构成

2017-10-13 19:50 357 查看


世界真的很大

匈牙利算法并不是一个有许多性质可以挖掘或者拓展的算法

但是对于其思路还是必须要熟悉才行。

邻接表的具体构建方式已经忘得差不多了,只是背的到

算是都复习了一遍

认真想起来其实也不是很难

看题先:

description:



input:



output :



要求选一种满足题意的“置换”,使得字典序最小

不懂置换是什么的话就看这里:传送门

所谓满足条件,就是说原先序列的一个点最多有两个可以去的地方

合理安排每个点去哪个点,看能不能使得所有点都有一个去处

如果方案有多种的话,就输出字典序最小的那一种

能不能使每一个点都有一个去处,翻译一下就是:

求一种方案使尽量多的点有去处且不重叠,然后看点数到不到n

二分图最大匹配,看匹配数有没有n。

到此,判断有没有一种合法方案就结束了,问题在于输出字典序最小的方案。

思路能一下想到的有两个

一是再找找一种方法去求这种最小的字典序的方案

感觉~~有点难

二呢,考虑到,求出最大匹配之后,match数组保存的匹配边已经是一种方案了,只不过可能不是最小而已,有没有什么办法暗箱操作一下匈牙利算法的过程,使得求出的正好是最小字典序呢?

怎么想都是第二种看起来实现难度低一点

复习一下匈牙利算法的实现流程:传送门

即,在每一次新点匹配的时候,如果能匹配,那自然是直接匹配,反之就会去看前面的点能不能让出这一个空位

即是说,以后匹配的点为优先。

那我们越想让哪个点取得最小值,越应该让其匹配顺序靠后,因为这样他收到其他店的干扰就越小。

由于是字典序最小,我们肯定是让编号小的取到较靠前的位置。

为了防止这些位置被其他点所占据,我们肯定是让编号小的点后匹配

那么对于一个点,我们肯定优先想让其匹配较靠前的点,这个怎么处理?

自然是在加边的时候判一下就好

考虑邻接表,有着“后加的边先跑”的原则。所以我们只需要在加边的时候,先加大的,再加小的,就行了

满足这两个约束条件后,跑出来的匹配是不是字典序最小的匹配呢?

答案是肯定的

若问为何?

他A了不是吗233

完整代码:

#include<stdio.h>

struct edge
{
int v,last;
}ed[4000010];

int n,ans=0,tot=0,num=0;
int head[200010],book[200010],match[200010];

void add(int u,int v)
{
num++;
ed[num].v=v;
ed[num].last=head[u];
head[u]=num;
}

int dfs(int u)
{
for(int i=head[u];i;i=ed[i].last)
{
int v=ed[i].v;
if(book[v]!=tot)
{
book[v]=tot;
if(!match[v] || dfs(match[v]))
{
match[u]=v,match[v]=u;
return 1;
}
}
}
return 0;
}

int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
int d;
scanf("%d",&d);
int a=(i+d)%n+n,b=(i-d+n)%n+n;
if(a>b) add(i,a),add(i,b);
else add(i,b),add(i,a);
}
for(int i=n-1;i>=0;i--)
{
tot++;
if(!match[i]) ans+=dfs(i);
}
if(ans<n) printf("No Answer");
else
{
for(int i=0;i<n;i++)
{
printf("%d",match[i]-n);
if(i!=n-1) printf(" ");
}
}
return 0;
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/


嗯,就是这样
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: