BZOJ 1562: [NOI2009]变换序列 二分图倒序匹配:匈牙利算法

血红的双手。 提交于 2019-11-26 16:49:46

title

BZOJ 1562
LUOGU 1963
Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

5
1 1 2 2 1

Sample Output

1 2 4 0 3

HINT

30%的数据中N≤50;
60%的数据中N≤500;
100%的数据中N≤10000。
BZOJ又直接把pdf的图片粘过来了。

analysis

这道题很妙啊!把一道看起来挺数论的题搞成二分图匹配。

抱歉,由于我们两个是在讨论网络流,所以,又在想这道题的网络流算法。

我想出:直接跑最大流,在选方案的时候,每个点都选相对字典序较小的那个点,应该就可以了吧。
\(Chdy\) 指出:万一你选了字典序较小的点,破坏了最大流呢?
我:...

然后,\(Chdy\) 想到了每个原数列最多可以连两条边,连向变换后的两个点,为什么?自己推这个式子:\(D(x,y)=min\left\{∣x−y∣,N−∣x−y∣\right\}\)

随后他想到的一些算法,都无法处理在选择字典序较小的点的同时保证最大流仍成立,所以...我们放弃了网络流写法,开心地奔向了匈牙利算法, ̄▽ ̄。

我想到的是,那就直接每确定一个点,跑一遍匹配。
\(Chdy\) :复杂度不够啊,能拿 \(70pts\) 就不错了。
我:...(你说怎么办呀...)

\(Chdy\):我觉得应该一边匹配,一边修正,不过匈牙利算法的复杂度应该是 \(O(N^3)\) 的吧,不太行。
我:邻接矩阵最坏\(O(N^3)\),邻接表\(O(NM)\)
\(Chdy\):那 OK 啦,可以看题解检验我们算法的正确性了。

于是翻开 \(BYVoid\)\(blog\),发现家宝写了四种写法,惊呼:神犇就是这样炼成的!
正解是什么倒序匹配,于是我便瞎证明一波,最后被吐槽:这不是显然正确吗。

好了,放代码吧,我可只写了倒序匹配这一种算法,别嫌弃。

神犇那里有四种解法:BYVoid

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}

template<typename T>inline void write(T x)
{
    if (!x) { putchar('0'); return ; }
    if (x<0) putchar('-'), x=-x;
    T num=0, ch[20];
    while (x) ch[++num]=x%10+48, x/=10;
    while (num) putchar(ch[num--]);
}

int ver[maxn],Next[maxn],head[maxn],len;
inline void add(int x,int y)
{
    ver[++len]=y,Next[len]=head[x],head[x]=len;
}

int match[maxn];
bool vis[maxn];
inline bool dfs(int x)
{
    for (int i=head[x]; i; i=Next[i])
    {
        int y=ver[i];
        if (!vis[y])
        {
            vis[y]=1;
            if (!match[y] || dfs(match[y]))
            {
                match[y]=x;
                match[x]=y;
                return true;
            }
        }
    }
    return false;
}

int s[maxn][2];
int main()
{
    int n;read(n);
    for (int i=0,c; i<n; ++i)
    {
        read(c);
        int t1=i-c;
        if (t1<0) t1+=n;
        int t2=i+c;
        if (t2>n-1) t2-=n;
        if (t1<t2) s[i][0]=t1,s[i][1]=t2;
        else s[i][0]=t2,s[i][1]=t1;
        add(i,s[i][1]+n);
        add(i,s[i][0]+n);
    }
    int ans=0;
    for (int i=n-1; i>=0; --i)
    {
        memset(vis,0,sizeof(vis));
        if (dfs(i)) ++ans;
    }
    if (ans<n) return puts("No Answer"),0;
    for (int i=0; i<n; ++i) write(match[i]-n),putchar(' ');
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!