P1963 [NOI2009]变换序列

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

 

题目描述

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

说明:对于两个变换序列 SS 和 TT ,如果存在 p<Np<N ,满足对于 i=0,1,\cdots p-1i=0,1,p1 , S_i=T_iSi=Ti 且 S_p<T_pSp<Tp ,我们称 SS 比 TT 字典序小。

输入输出格式

输入格式:

 

第一行包含一个整数 NN ,表示序列的长度。接下来的一行包含 NN 个整数 D_iDi ,其中 D_iDi 表示 ii 和 T_iTi 之间的距离。

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 复制
5
1 1 2 2 1
输出样例#1: 复制
1 2 4 0 3

说明

对于30%的数据,满足:N<=50;

对于60%的数据,满足:N<=500;

对于100%的数据,满足:N<=10000。

 

/*
题里面对D(x,y)的定义那一长句,一开始没看明白,但实际会发现是一个环,而对于每一个点u,符合给定距离的点都有且只有2个(v1&&v2),连u->v1和u->v2。
连边的时候注意先连终点序号大的,这样才能保证遍历时从小到大。
为什么要做这个操作呢?因为要求输出字典序最小的解,就必须保证较小的点优先匹配较小的点。
匈牙利算法的过程,总是通过调整先前匹配的点,而使当前点尽量不动。
所以,匈牙利算法倒序跑,增广时优先选小的点,这样就能够保证「越小的点越能匹配较小的点」,从而实现字典序最小化。(贪心思想)
最终输出X部每个点的匹配点即可。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
using namespace std;

const int N=20010;

bool vis[N];
int n,ans;
int head[N],num_edge;
int match[N];
struct edge
{
    int v,nxt;
}edge[N];

int solve(int x)
{
    if(x<0)
        x+=n;
    if(x>=n)
        x-=n;
    return x+n;
}

void add_edge(int u,int x)
{
    int v1=solve(u+x),v2=solve(u-x);
    if(v1<v2)
        swap(v1,v2);
    edge[++num_edge].v=v1;
    edge[num_edge].nxt=head[u];
    edge[++num_edge].v=v2;
    edge[num_edge].nxt=num_edge-1;
    head[u]=num_edge;
}

bool dfs(int u)
{
    for(int i=head[u],v;i;i=edge[i].nxt)
    {
        if(!vis[v=edge[i].v])
        {
            vis[v]=1;
            if(!match[v]||dfs(match[v]))
            {
                match[u]=v,match[v]=u;
                return 1;
            }
        }
    }
    return 0;
}

bool Hungary()
{
    for(int i=n-1;i>=0;--i)
        if(!match[i])
        {
            memset(vis,0,sizeof vis);
            ans+=dfs(i);
        }
    return n==ans;
}

int main()
{
    scanf("%d",&n);
    for(int i=0,x;i<n;++i)
    {
        scanf("%d",&x);
        add_edge(i,x);
    }
    if(Hungary())
        for(int i=0;i<n;++i)
            printf("%d ",match[i]-n);
    else
        printf("No Answer");
    return 0;
}

 

posted @ 2018-05-12 17:51  whymhe  阅读(373)  评论(1编辑  收藏  举报