[NOI2009] 变换序列

[NOI2009] 变换序列

题意:

给定 \(D(i,T_i)\) 表示 \(i\)\(T_i\) 在长度为 \(n\) 上的环的最小距离。

\(T_i\) 的字典序最小的排列。

分析:

当知道 \(D(i,T_i)\)

\(|i-T_i|\leq N-|i-T_i|\) 时, \(T_i=i-D\)\(T_i=i+D\)

\(|i-T_i|>N-|i-T_i|\) 时,\(T_i=i-D+N\)\(T_i=N-D+i\)

最终每个 \(i\) 只可能和一个 \(T_i\) 相互对应。

对于每一个 \(i\) 向可行的 \(T_i\) 跑匈牙利算法即可算出来一组解。因此可判断有解或无解。

考虑求出来一组字典序最小的解:

其实也很简单,从 \(1-n\) 依次将 \(vector[i]\) 中连接的点从小到大排列,重新匹配点时,一定选取的是最小最合适的点。因为如果选择字典序更大的点, 肯定不优。

记得储存一下建边方案即可。

代码:

#include<bits/stdc++.h>

using namespace std;
const int N=1e4+5;

vector<int> g[N];
int n,m,ans;
int vis[N],match[N],a[N];

bool dfs(int x){
    for(auto y:g[x]){
        if(vis[y]) continue;
        vis[y]=1;
        if(match[y]==-1||dfs(match[y])){
            match[y]=x; a[x]=y; return true;
        }
    }
    return false;
}

int main(){
    cin>>n;
    for(int i=0,x,l,r;i<n;i++){
        scanf("%d",&x); 
        if(i-x>=0) g[i].push_back(i-x);
        if(i+x<n) g[i].push_back(i+x); 
        if(i+x-n>=0) g[i].push_back(i+x-n);
        if(n-x+i<n) g[i].push_back(n-x+i);
    }
    memset(match,-1,sizeof(match));
    for(int i=0;i<n;i++) sort(g[i].begin(),g[i].end());
    for(int i=n-1;i>=0;i--){memset(vis,0,sizeof(vis)); if(dfs(i)) ans++;}
    if(ans!=n) puts("No Answer");
    else for(int i=0;i<n;i++) printf("%d ",a[i]); puts("");
    system("pause");
    return 0;
}
posted @ 2021-11-06 16:02  Evitagen  阅读(46)  评论(0编辑  收藏  举报