[NOI2009]变换序列

题目大意

分析

一道二分图匹配的题,通过纸上化简,可得一个点i只会与(i+k)%n和(i-k+n)%n这两个点连边,然后对其进行匈牙利,因为顾及到最后要求,所以从后往前匹配,若匹配失败直接输出“No answer”,在匹配过程中要维护两个数组,一个是用来判断是否可以喊一个点进行匹配,另一个则是维护答案,若匹配成功,返回true,否则false,同时在遍历所连得边时,要先遍历较小的一个,这样有利于是答案的字典序最小。
上代码,时间100ms。

#include<bits/stdc++.h>
using namespace std;
int n,ver[20001][2],ma[20001],z,v[20001],mm=0,mq[20001];
void lb(int x,int y){
    int a=(x+y)%n,b=(x-y+n)%n;
    ver[x][0]=min(a,b);
    ver[x][1]=max(a,b);
}
bool dfs(int a){
    for(int i=0,y;i<2;i++){
        if(!v[y=ver[a][i]]){
            v[y]=1;
            if((ma[y]==-1)||dfs(ma[y])){
                ma[y]=a;
                mq[a]=y;
                return true;
            }
        }
    }
    return false;
}
bool cl(){
    memset(ma,-1,sizeof(ma));
    for(int i=n-1;i>=0;i--){
        memset(v,0,sizeof(v));
        if(!dfs(i)) return false;
    }
    return true;
}
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&z);
        lb(i,z);
    }
    if(!cl()){
        printf("No Answer");
        return 0;
    }
    for(int i=0;i<n;i++) printf("%d ",mq[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sjzezwzy/article/details/80886322