左右节点的设定和遍历图的方向都无所谓,本质在于维护字典序最小时候的遍历。
因为要求是最终字典序最小,所以,维护的时候,要从末尾也就是n-1节点遍历到0节点去维护,这样实际字典序才会使最小的,因为见了题解有误而且没有分析,这里就说一声吧。 顺便贴个代码
#include <bits/stdc++.h>
using namespace std;
#define N 20210
#define mt(x) memset(x, 0, sizeof x)
typedef long long ll;
void cn(ll x) { cout << x << endl; }
void cs(string x) { cout << x << endl; }
vector<int> vc[N];
int vis[N], ans[N];
/*
样例一:
10
1 5 4 2 3 1 4 3 1 4
1 6 8 5 7 4 2 0 9 3
*/
bool dfs(int x)
{
for (int i = 0; i < vc[x].size(); ++i)
{
int y = vc[x][i];
if (!vis[y])
{
vis[y] = 1;
if (!ans[y] || dfs(ans[y]))
{
ans[y] = x;
ans[x] = y;
return true;
}
}
}
return false;
}
void solve()
{
/*
analyse:
有x序列和t序列
要构造出一个序列
使得min(\i-ti\,n-\i-ti\)为最小值
给定每一位值di=min(\i-ti\,n-\i-ti\)
想一想似乎也对的上是一个最大匹配问题
如果不是n对匹配,那么就不存在
另外一个问题是,如何保证字典序最小
找到最大匹配后,调整匹配
以此失配每个点,看能否找更小值。。找到就更新
否则恢复匹配
*/
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
{
//首先是根据di建边
int x;
cin >> x;
int u = i - x, v = i + x;
if (u < 1)
u += n;
if (v > n)
v -= n;
//这里必须排序,影响后面维护字典序最小的ans选择
//也可以先大后小,这样ans就该选择vc[i][1]
vc[i].push_back(min(u + n, v + n));
vc[i].push_back(max(u + n, v + n));
}
int sum = 0;
for (int i = 1; i <= n; ++i)
{
mt(vis);
if (dfs(i))
sum++;
}
if (sum ^ n)
cs("No Answer");
else
{
/*
//排序前输出检测
for(int i=1;i<=n;++i)
{
cout<<ans[i]-n-1;
if(i^n)cout<<' ';
else cs("");
}
cs("-------------");
//*/
//找更小值
for (int i = n; i >= 1; --i)//重点在这里,必须逆序
{
/*
原因是因为
如果从头开始考虑字典序最小
实际字典序权重是在末尾
所以,倒着来
本人看了题解,好些都没注意到这一点
*/
if (ans[i] != vc[i][0]) //不是最小值
{
mt(vis);
//0-p
//1-i
int p = ans[vc[i][0]]; //记录原配
ans[vc[i][0]] = i; //人工建立匹配
ans[vc[i][1]] = 0; //失配
vis[vc[i][0]] = 1;
if (dfs(p))
ans[i] = vc[i][0]; //victory
else
{
//恢复
ans[vc[i][0]] = p;
ans[vc[i][1]] = i;
}
}
}
for (int i = 1; i <= n; ++i)
{
cout << ans[i] - n - 1;
if (i ^ n)
cout << ' ';
else
cs("");
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}