[BZOJ 1562][NOI 2009]变换序列(二分图匹配)

来源:互联网 发布:类似知乎的问答型app 编辑:程序博客网 时间:2024/06/06 07:25

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=1562

题目大意

有一个n序列A,对于任意的Ai1<=i<=n,1<=Ai<=n,1<=i,j<=n,i!=jAi!=Aj。对其中每个元素进行加di mod n或者是减dinmod n的操作后,变成一个新的序列T,要你判断这样的T是否存在,以及最小字典序的T

题目思路

如果只要判断合法性,这就是个裸的二分图匹配问题,将AAiT(Ai+di) mod n,(Aidi+n) mod n连边,然后判断二分图是否能完全匹配即可。不过如果要求字典序最小的T也很简单,我们只需要保证邻接表中的每条边按照它们的终点升序排序,那么在二分图匹配时,如果没有增广的情况,每个x侧点选择的y侧点一定都是最小的。我们再从x侧点的最后一个点开始,从后往前进行增广,便能使得前面的点找不到匹配的y侧点时,后面的点会由最小的y侧对应匹配点变成大一点点的匹配点,而前面的点就能选择更小的那个匹配点了,这样保证前面的点找到的匹配点比后面的点找到的匹配点更优,便能满足最小字典序的要求了(说起来很绕口额。。。大家自己意识流一下便能明白为什么这是正确的了)
然后很无脑地贡献了4发PE,坑爹的BZOJ居然不能输出行末空格和文末换行

代码

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#include <vector>#define MAXE 1100000#define MAXV 31000using namespace std;int n;int pre[MAXV];bool visit[MAXV];vector<int>G[MAXV];bool dfs(int u){    for(int i=0;i<(int)G[u].size();i++)    {        int v=G[u][i];        if(!visit[v])        {            visit[v]=true;            if(pre[v]==-1||dfs(pre[v]))            {                pre[v]=u;                pre[u]=v;                return true;            }        }    }    return false;}int main(){    memset(pre,-1,sizeof(pre));    scanf("%d",&n);    for(int i=1;i<=n;i++)    {        int d;        scanf("%d",&d);        int a=i-d,b=i+d;        if(a<=0) a+=n;        if(b>n) b-=n;        a+=n,b+=n; //与原有的1~n的数字区分开        if(a>b) swap(a,b); //保证a<b,这样邻接表里的边的终点才能是字典序        G[i].push_back(a),G[i].push_back(b);    }    for(int i=n;i>=1;i--) //注意是从后往前做二分图匹配    {        memset(visit,false,sizeof(visit));        if(!dfs(i))        {            printf("No Answer");            return 0;        }    }    for(int i=1;i<n;i++)        printf("%d ",pre[i]-n-1);    printf("%d",pre[n]-n-1);    //printf("\n");    return 0;}
0 0
原创粉丝点击