noi2009变换序列
一、题目
对于N个整数0,1,…,N-1,一个变换序列T可以将i变成Ti,其中:Ti∈{0,1,…,N-1}且Ui=1 to 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”(不含引号)。
输出文件中相邻两个数字之间用一个空格分开,行末不包含多余空格。
5
1 1 2 2 1
1 2 4 0 3
对于30%的数据,满足:N<=50;
对于60%的数据,满足:N<=500;
对于100%的数据,满足:N<=10000。
分类标签 Tags 点此展开
二、分析
1、题目描述:
我现在简要述说一下这一题的意思:题目的意思就是给出x对y的对应关系:,现在给出D(x,y)和x,求y,并且要求字典序最小的y。(x这组数是从0到n-1,y这组数属于0到n-1)
下面分析一下样例就更加方便理解这个题目了:
X |
0 |
1 |
2 |
3 |
4 |
D(x,y) |
1 |
1 |
2 |
2 |
1 |
Y |
1 |
2 |
4 |
0 |
3 |
表一
样例输入中给了n为5,,所以表一第一行x的值也是从0到4。表一的第二行D(x,y)就是输入数据的x,y的关系。最后需要我们求的就是表一第三行的y的数据。
我们可以看到上表中的(每列)每组x,y,D(x,y)都是满足关系,比如x=0,D(x,y)=1,y=1这一列,
|x-y|=1,N-|x-y|=4,故D(x,y)=1,所以关系成立,后面的以此类推。
2、题目思考:
我们在仔细来看一下关系式,发现对于这样的一个关系式,我们知道D(x,y)和x,求y的话,y最多有四个值,不过其实仔细拿实例出来分析之后,就发现这四个值是可以变成两个值的。
看到题目,发现题目是一个赤裸裸的二分匹配。这个题目可以用匈牙利算法来做,匈牙利算法的时间复杂度是O(nm),在这里是完全ok的。
这个题目还有另外一个难点,就是要求字典序最小的y,其实求最小的y可以在寻找交错路(匹配关系)的时候倒序寻找,不过这样做的时候要注意,在存边关系的连接表的时候要注意从小往大存,确保从字典序小的边找起。
因为如果正序查找,按照匈牙利算法的算法规则,那么一定是找到的字典序最大的那个。
问题:求二分图最大匹配可以用最大流(Maximal Flow)或者匈牙利算法(Hungarian Algorithm)
3、总结:
总结:其实这个题目就是二分匹配中的匈牙利算法,套用下匈牙利的模板,再想好怎么输出字典序最小的y就ok了。
三、代码
1 #include<iostream> 2 #include<iomanip> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstring> 6 #include<cstdlib> 7 #include<algorithm> 8 #include<queue> 9 #include<map> 10 #define MAXN 10010 11 using namespace std; 12 13 14 15 int graph[MAXN][2];//每个点最多连出2条边,存边时保证graph[i][0] 16 17 int vis[MAXN];//表示节点是否被访问 18 int match[MAXN];//Y集合中的点i与X集合的match[i]匹配 19 int ans[MAXN];//用于输出结果 20 int n; 21 22 //用来构建关系,确保字典序小的存在前面 23 void addedge(int i,int ver) 24 { 25 if(graph[i][0]<0) 26 { 27 graph[i][0]=ver; 28 return; 29 } 30 else if(graph[i][0]>ver) 31 { 32 graph[i][1]=graph[i][0]; 33 graph[i][0]=ver; 34 } 35 else graph[i][1]=ver; 36 } 37 //匈牙利算法中的寻找交错路 38 bool crosspath(int ns) 39 { 40 int j,nt; 41 for(j=0;j<=1;j++)//每个点优先匹配编号较小的点(graph[i][0] 42 { 43 nt=graph[ns][j]; if(nt<0) continue; 44 if(vis[nt]) continue; 45 vis[nt]=1; 46 if(match[nt]<0||crosspath(match[nt])) 47 { 48 match[nt]=ns; 49 return true; 50 } 51 } 52 return false; 53 } 54 //匈牙利算法倒叙从每个点找交错路,确保求出最小字典序y 55 int find() 56 { 57 memset(match,128,sizeof(match)); 58 memset(vis,0,sizeof(vis)); 59 int i,tot=0; 60 for(i=n-1;i>=0;i--)//倒叙从每个点找交错路 61 { 62 if(crosspath(i)) tot++; 63 memset(vis,0,sizeof(vis)); 64 } 65 return tot; 66 } 67 68 69 70 int main() 71 { 72 //freopen("in.txt","r",stdin); 73 //输入数据以及确定每个D(x,y)和x对应的y,用于构造二分图 74 int i,qh,l,r; 75 scanf("%d",&n); 76 memset(graph,128,sizeof(graph)); 77 for(i=0;i<n;i++) 78 { 79 scanf("%d",&qh);//qh为读入的d[i],从i向i+d[i],i-d[i]连边(要取模) 80 l=((i-qh)%n+n)%n; 81 r=((i+qh)%n+n)%n; 82 addedge(i,l); 83 if(r!=l) addedge(i,r); 84 } 85 if(find()<n)//未能完全匹配则无解。 86 { 87 printf("No Answer"); 88 exit(0); 89 } 90 //统计答案输出。 91 for(int i=0;i<n;i++) ans[match[i]]=i; 92 printf("%d",ans[0]); 93 for(int i=1;i<n;i++) printf(" %d",ans[i]); 94 return 0; 95 }