zoukankan      html  css  js  c++  java
  • P1963 [NOI2009]变换序列

    对于(N)个整数(0, 1, cdots, N-1,)一个变换序列(T)可以将(i)变成(T_i),其中 (T_i in { 0,1,cdots, N-1})(igcup_{i=0}^{N-1} {T_i} = {0,1,cdots , N-1})(forall x,y in {0,1,cdots , N-1}),定义(x)(y)之间的距离(D(x,y)=min{|x-y|,N-|x-y|})。给定每个(i)(T_i)之间的距离(D(i,T_i)),你需要求出一个满足要求的变换序列T。如果有多个满足条件的序列,输出其中字典序最小的一个。

    说明:对于两个变换序列(S)(T),如果存在(p<Np<N),满足对于(i=0,1,cdots p-1)(S_i=T_i)(S_p<T_p),我们称(S)(T)字典序小。

    输入格式:

    第一行包含一个整数(N),表示序列的长度。接下来的一行包含(N)个整数(D_i),其中(D_i)表示(i)(T_i)之间的距离。

    输出格式:

    如果至少存在一个满足要求的变换序列(T),则输出文件中包含一行(N)个整数,表示你计算得到的字典序最小的(T);否则输出No Answer(不含引号)。注意:输出文件中相邻两个数之间用一个空格分开,行末不包含多余空格。

    输入样例#1:

    5
    1 1 2 2 1
    

    输出样例#1:

    1 2 4 0 3
    

    说明

    对于(30\%)的数据,满足:(N<=50)

    对于(60\%)的数据,满足:(N<=500)

    对于(100\%)的数据,满足:(N<=10000)


    这个题目相当优秀,它能够很好的帮你理解匈牙利算法的本质。

    首先看到这个题,可以很显然的发现这是一个裸的二分图匹配问题。但是牵涉到字典序最小的话,就需要考虑其他的操作了。

    最开始我考虑的方法是从前到后匹配:

    • 如果两个都没有匹配,那么选数字小的那个
    • 如果都匹配了,选择如果匹配,会产生的影响最早数字最靠后的那个
    • 如果一个匹配一个没匹配,先选没匹配的那个

    写了一堆特判之后WA成沙雕,还满的一批,因为我要在匹配之前提前模拟一遍第二种,然后就多了一堆奇奇怪怪的东西。

    相比之下,正解的想法就相当有趣。

    匈牙利算法本身就是从前向后依次尝试匹配。所以如果想要保证在前面的数字尽可能小,那么只需要让它优先匹配标号小的节点就好。但是这里存在一个问题:如果从前到后依次匹配的话,为了保证不把前面的最优选择替换,你就必须特判很多东西。实际上,只需要从后向前匹配,前面的尽可能选最小数字,如果可以替换,把后面选择的直接替换即可。这样得到的一定是最优解。

    Code:

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define MAXN 10010
    #define _mod(x) ((x)%n+n)%n
    using namespace std;
    int n,T[MAXN],vis[MAXN<<1],v[MAXN][2],match[MAXN<<1];
    
    bool dfs(int x){
    //	寻找x的匹配
        for(int i=0;i<2;++i){
            if(!vis[n+v[x][i]]){
                vis[n+v[x][i]]=true;
                if(match[n+v[x][i]]==-1 || dfs(match[n+v[x][i]])){
                    match[x]=n+v[x][i];
                    match[n+v[x][i]]=x;
                    return true;
                }
            }
        } 
        return false;
    }
    
    int main(){
        scanf("%d",&n);
        memset(match,-1,sizeof(match));
        for(int i=0;i<n;++i){
            scanf("%d",&T[i]);
            v[i][0]=_mod(i+T[i]);
            v[i][1]=_mod(i-T[i]);
            if(v[i][0]>v[i][1]){
                swap(v[i][0],v[i][1]);
            }
            //临时储存两个终点
        }
        //按位匹配
        for(int i=n-1;i>=0;--i){
            memset(vis,0,sizeof(vis));
            if(!dfs(i)){
                puts("No Answer");
                return 0;
            }
        }
        for(int i=0;i<n;++i){
            printf("%d ",match[i]-n);
        } 
    } 
    
  • 相关阅读:
    shader之渐变长方体实现(threejs)
    shader之threejs应用
    shader之cesium应用
    pip install -- Failed building wheel for XXX
    pycharm -- 界面乱码
    Android Studio -- 优化速度
    django -- ImageField 上传图片修改头像
    AI -- 回溯法解决四皇后问题
    Android Studio -- 真机测试
    傻瓜函数式编程
  • 原文地址:https://www.cnblogs.com/maomao9173/p/10017933.html
Copyright © 2011-2022 走看看