zoukankan      html  css  js  c++  java
  • noi2009变换序列

    noi2009变换序列

    一、题目

    1843 变换序列

     

    2009年NOI全国竞赛

     时间限制: 1 s
     空间限制: 128000 KB
     题目等级 : 大师 Master
     
     
     
    题目描述 Description

    对于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,&hellip;,p-1,Si=Ti且Sp<Tp,我们称S比T字典序小。

    输入描述 Input Description

    输入文件中的第一行为一个整数N,表示序列的长度。

    接下来的一行为N个整数Di,其中:Di表示i和Ti之间的距离。

    输出描述 Output Description

    如果至少存在一个满足要求的变换序列T,则输出一行为N个整数,表示你计算得到的字典序最小的T;否则输出&ldquo;No Answer&rdquo;(不含引号)。

    输出文件中相邻两个数字之间用一个空格分开,行末不包含多余空格。

    样例输入 Sample Input

    5

    1 1 2 2 1

    样例输出 Sample Output

    1 2 4 0 3

    数据范围及提示 Data Size & Hint

    对于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 }
  • 相关阅读:
    bzoj3380+3381+3382+3383 Usaco2004 Open
    浅谈树链剖分(C++、算法、树结构)
    小学奥数 最大公约数与最小公倍数
    小学奥数 质数的和与积
    小学奥数 分苹果
    小学奥数 连乘积末尾0的个数
    小学奥数 李白的酒
    小学奥数 回文数个数
    小学奥数 等差数列末项计算
    小学奥数 地球人口承载力估计
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/7402285.html
Copyright © 2011-2022 走看看