(基本最大匹配+维护字典序)解法的关于(左右节点,遍历方向,字典序选择)分析
查看原帖
(基本最大匹配+维护字典序)解法的关于(左右节点,遍历方向,字典序选择)分析
378678
mimiyou楼主2021/7/24 14:00

左右节点的设定和遍历图的方向都无所谓,本质在于维护字典序最小时候的遍历。

因为要求是最终字典序最小,所以,维护的时候,要从末尾也就是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;
}
2021/7/24 14:00
加载中...