[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;
}
不关注的有难了😠😠😠https://b23.tv/hoXKV9