【题解】P1963 [NOI2009]变换序列

https://www.luogu.org/problemnew/show/P1963

变换序列
【问题描述】
对于N个整数0,1,…,N-1,一个变换序列T可以将i变成Ti,其中:Ti∈{0,1,…,N-1}且 {Ti}={0,1,…,N-1}。 x,y∈{0,1,…,N-1},定义x和y之间的距离D(x,y)=min{|x-y|,N-|x-y|}。给定每个i和Ti之间的距离D(i,Ti),你需要求出一个满足要求的变换序列T。如果有多个满足条件的序列,输出其中字典序最小的一个。
说明:对于两个变换序列S和T,如果存在p<N,满足:对于i=0,1,…,p-1,Si=Ti且Sp<Tp,我们称S比T字典序小。
【输入】
输入文件中的第一行为一个整数N,表示序列的长度。
接下来的一行为N个整数Di,其中:Di表示i和Ti之间的距离。
【输出】
如果至少存在一个满足要求的变换序列T,则输出一行为N个整数,表示你计算得到的字典序最小的T;否则输出“No Answer”(不含引号)。
【输入输出样例】
transform.in transform.out
5
1 1 2 2 1
1 2 4 0 3
【数据说明】
对于30%的数据,满足:N<=50;
对于60%的数据,满足:N<=500;
对于100%的数据,满足:N<=10000。

这道题,首先我们先看一下题目,考场上遇到时候,第一反应是用搜索,于是。。。打了个搜索。。。然后完美的由于复杂度爆了。。(搜索应该三十分,但我的复杂度感人只有十分

搜索就不多说,直接讲一下正解,正解是用二分图匹配。
每个元素可以和另外两个元素对应,两个集合相互可以链接,集合内部没有联系,这就形成了一个很好的二分图匹配,二分图的匹配如果不知道的话emmmm。。。。https://blog.csdn.net/dark_scope/article/details/8880547 大佬讲解的匈牙利算法
知道了这是二分图,那么就有了一个大致思路,但是,字典序该如何维护呢,我们考虑匈牙利算法的原理,能连则连(这是一种贪心的思想)那么后面连上的会找到最小的可以连上的对象,所以,我们只需要反向搜,就可以找到最小字典序了

#include<iostream>
#include<cstdio>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define FR(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=100100;
int n,v,d[N],vi[N],to[N],lk[N],s[N][2];
bool hun(int u){
	FOR(i,0,1)if(!vi[s[u][i]]){
		int v=s[u][i];vi[v]=1;
		if(lk[v]==-1 || hun(lk[v])) {lk[v]=u;to[u]=v;return 1;}
	}return 0;
}
int main(){
	scanf("%d",&n);
	FOR(i,0,n-1) scanf("%d",&d[i]);
	FOR(i,0,n-1){
		s[i][0]=(i+d[i]+n)%n;s[i][1]=(i-d[i]+n)%n;
		if(s[i][0]>s[i][1]) swap(s[i][0],s[i][1]);
		lk[i]=to[i]=-1;
	}
	FR(i,n-1,0){
		FOR(j,0,n-1) vi[j]=0;
		if(!hun(i)) {printf("No Answer");return 0;}
	}
	FOR(i,0,n-1) printf("%d ",to[i]);
}

猜你喜欢

转载自blog.csdn.net/weixin_43464026/article/details/83549304