【NOI2009】Bzoj1562&Codevs1843 变换序列
Position:
List
Description
Input
输入文件 transform.in 的第一行包含一个整数 N,表示序列的长度。接下来的
一行包含 N 个整数 D i ,其中 D i 表示 i 和 T i 之间的距离。
Output
输出文件为 transform.out。
如果至少存在一个满足要求的变换序列 T,则输出文件中包含一行 N 个整数,
表示你计算得到的字典序最小的 T;否则输出”No Answer”(不含引号)。注意:
输出文件中相邻两个数之间用一个空格分开,行末不包含多余空格。
Sample Input
5
1 1 2 2 1
Sample Output
1 2 4 0 3
HINT
30%的数据中 N≤50;
60%的数据中 N≤500;
100%的数据中 N≤10000。
Solution
观察题中给的式子,发现每个位置可以填两个数,但只能填一个数,求是否每个位置可以填上一个不重复的0~n-1的数,并保证字典序最小,也就是填在前面的数要尽量小。
最小字典序的匈牙利,贪心发现可以倒着来跑匈牙利,保证对于当前位置的数尽量小,替换掉的是后面的数,后面的数也保证了尽量小。而正着来后面的数可能会替换掉之前的数,不满足字典序最小。
官方题解%莫队
Transform解题报告
湖南省长郡中学 莫涛
【题目大意】
给定n和{Ti},求一个字典序最小的0..n-1的排列{Pi},使{Pi}满足{,或指出无解。
n<=10000。
【解法分析】
不难看出,所给限制即i与Pi的环上距离为Ti,故对于任意i,至多存在两个Pi(从i顺时针或逆时针移动Ti距离)满足限制。
构建二分图模型,以0,1..n-1为X节点,0,1..n-1为Y节点,若Pi=j满足限制则由X的i节点向Y的j节点连一条边,则问题转化为判断是否存在完备匹配,若存在则求出字典序最小的匹配方案。
【算法一】
首先使用匈牙利算法求解,若不存在完备匹配则返回无解,否则按如下流程调整使得求得字典序最小解:
for do
·if i与ai,bi(ai>bi)有边且i与ai匹配 then
·删去匹配边(i,ai)并暂时禁用该边
·令j为原来与bi匹配的点
·将i与bi匹配
·if 从j出发可以找到增广路 then
·保留该次增广,并在以后的增广中禁止改变匹配边(i,bi)
·else 还原上述改动
该算法的核心思想即利用字典序比较的特性,从前往后尝试改小每一位,若更改后剩余部分存在可行解则永久保留该次更改,否则撤销。
由于每个点至多有两条出边,故只在取了匹配了编号较大的点时尝试匹配编号较小的点,而剩下部分有可行解的条件为存在完备匹配,若直接用匈牙利算法求解则时间复杂度为O(n^3),但我们知道:
对于任意残量网络,使用增广路算法均可求得最大流(最大匹配)。
而对于任意k(k>i),k均已匹配,故只需从i出发寻找增广路即可,这样时间复杂度就是O(n^2)了,可以通过测试数据。
【算法二】
考虑到该图非常特殊(X节点出度至多为2),可以从图论的角度求解。
首先,如果存在度为1的点,那么可以直接确定匹配,故可以使用队列持续删去度为1的点及其确定的匹配点及于它们相关的边,直到不存在度为1的点。
然后,如果此时存在度为0的解,那么可以直接返回无解。
最后,由于所有的X节点度大于1且不大于2,故度均为2;而所有Y节点度大于1且∑d(X)=∑d(Y),故所有Y节点度均为2。
一个所有点度均为2的图必然是若干不相交的环。而一个环中只存在两种匹配方案,直接选择较小者即可。
该算法时间复杂度仅为O(n)。
【算法三】
算法二虽然优秀但实现较繁琐,不妨将其与算法一结合起来。
考虑直接使用二分图匹配,对于算法二种在第一步删去的点,必然能求出正确的匹配,解不优只会发生在环中。尝试改进匹配算法:
从大到小增广点i,寻找增广路时优先走向编号较小的点。
证明:
1.对于任一环,匹配的最后结果由最后增广的点决定,由上述算法的特性知,一定会找到该环内的最优解。
2.对于环外一点,若存在经过该环的增广路,则该环内某X节点存在连向环外的边,那么它的度数超过2,矛盾,故不会因为环外某点的增广使得该环的匹配方案改变。
虽然该算法时间复杂度为O(n^2),但思维复杂度与编程复杂度均很低,不失为考场上的好算法。
【小结】
算法一具有普适性,其核心思想也广泛应用于求字典序最小方案的各种问题中;算法二充分挖掘了本题中图的特殊性,时间复杂度达到理论下界;算法三主要利用了贪心思想,非常简洁,但其证明却不是那么显然。
值得一提的是,算法三的正确性是基于该图的特殊性的,因而缺少可推广性,即不能解决一般二分图的字典序最小的匹配,下图即一个反例:
使用算法三将求出(1,1)(2,3)(3,2),而最优解显然是(1,1)(2,2)(3,3)。
此外,本题还可以利用2-SAT模型求解,但该模型在各方面均不优于二分图模型,故略去不提。
Code
// <transform.cpp> - Fri Sep 23 08:09:06 2016
// This file is made by YJinpeng,created by XuYike's black technology automatically.
// Copyright (C) 2016 ChangJun High School, Inc.
// I don't know what this program is.
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#define MOD 1000000007
#define INF 1e9
using namespace std;
typedef long long LL;
const int MAXN=100010;
const int MAXM=100010;
inline int max(int &x,int &y) {return x>y?x:y;}
inline int min(int &x,int &y) {return x<y?x:y;}
inline int gi() {
register int w=0,q=0;register char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')q=1,ch=getchar();
while(ch>='0'&&ch<='9')w=w*10+ch-'0',ch=getchar();
return q?-w:w;
}
int n,k,t[5],match[MAXN];
vector<int>b[MAXN];bool f[MAXN];
void work(int i,int a){
int b=i+a;
if(b<n)t[++k]=n+b;b=i-a;
if(b>=0)t[++k]=n+b;
}
inline void add(int v,int u){
b[u].push_back(v);b[v].push_back(u);
}
inline bool dfs(register int x){
if(f[x])return 0;
int num=b[x].size();f[x]=true;
for(int i=0;i<num;i++){
int nex=b[x][i];
if(match[nex]==-1||dfs(match[nex])){
match[x]=nex;match[nex]=x;f[x]=0;return 1;
}
}f[x]=0;
return 0;
}
void pri(){printf("No Answer");exit(0);}
int main()
{
freopen("transform.in","r",stdin);
freopen("transform.out","w",stdout);
n=gi();
for(int i=0;i<n;i++){
k=0;int a=gi();work(i,a);
work(i,n-a);
sort(t+1,t+1+k);for(int o=1;o<=k;o++)add(i,t[o]);
}
for(int i=0;i<MAXN;i++)match[i]=-1;
for(int i=n-1;i>=0;i--)
if(match[i]==-1)if(!dfs(i))pri();
for(int i=0;i<n-1;i++)printf("%d ",match[i]-n);
printf("%d",match[n-1]-n);
return 0;
}