BZOJ 1562 [NOI 2009] 二分图 解题报告

本文针对NOI2009变换序列问题,介绍了一种利用二分图匹配算法寻找最小字典序变换序列的方法。通过合理排序邻接表及逆向增广路径策略,确保了匹配结果的最优性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1562: [NOI2009]变换序列

Description

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Sample Input

5
1 1 2 2 1

Sample Output

1 2 4 0 3

【解题报告】
题目大意:有一个n的排列,对其中每个元素进行加di mod n或者是减di加n后mod n的操作后,变成一个新的序列T,要你求最小字典序的T。
如果只要判断合法性,这就是个裸的二分图匹配问题,将A中的每个Ai与T中的数字(Ai+di) mod n,(Ai−di+n) mod n连边,然后判断二分图是否能完全匹配即可。不过如果要求字典序最小的T也很简单,我们只需要保证邻接表中的每条边按照它们的终点升序排序,那么在二分图匹配时,如果没有增广的情况,每个x侧点选择的y侧点一定都是最小的。我们再从x侧点的最后一个点开始,从后往前进行增广,便能使得前面的点找不到匹配的y侧点时,后面的点会由最小的y侧对应匹配点变成大一点点的匹配点,而前面的点就能选择更小的那个匹配点了,这样保证前面的点找到的匹配点比后面的点找到的匹配点更优,便能满足最小字典序的要求了
注意卡输出。

代码如下:

/**************************************************************
    Problem: 1562
    User: onepointo
    Language: C++
    Result: Accepted
    Time:40 ms
    Memory:1368 kb
****************************************************************/

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 20010

int n;
int cnt,head[N],vis[N],match[N];
struct Edge{int to,nxt;}e[N<<1];

void adde(int u,int v)
{
    e[++cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}
bool dfs(int u,int flag)
{
    for(int i=head[u];~i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(vis[v]==flag) continue;
        vis[v]=flag;
        if(match[v]==-1||dfs(match[v],flag))
        {
            match[v]=u;
            match[u]=v;
            return 1;
        }
    }
    return 0;
}
int main()
{
    cnt=0;
    memset(head,-1,sizeof(head));
    memset(match,-1,sizeof(match));
    scanf("%d",&n);
    for(int i=0;i<n;++i)
    {
        int d;scanf("%d",&d);
        int t1=max((i+d)%n,(i-d+n)%n);
        int t2=min((i+d)%n,(i-d+n)%n);
        adde(i+n,t1);adde(i+n,t2);
    }
    for(int i=n-1;i>=0;--i) 
    {  
        if(match[i+n]==-1&&!dfs(i+n,i+n)) 
        {  
            puts("No Answer");  
            return 0;  
        }    
    }  
    for(int i=n;i<(n<<1);++i) 
    {  
        printf("%d",match[i]);
        printf((i==(n<<1)-1)?"\n":" ");  
    }  
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值