[GDOI模拟2015.08.08]排列

题目大意

给定对于一个 1 n 的排列 {an} m 个形如下述的约束:
(1,x,y,v) 排列的第 x 个数到第 y 个数之间最大值为 v
(2,x,y,v) 排列的第 x 个数到第 y 个数之间最小值为 v
要求还原这个排列。
方案可能很多,输出一种即可。如果没有符合要求的排列,则输出-1。

1n200,0m40000

题目分析

有“约束条件“,再看到 n 200 以内,那这题应该最有可能为网络流。
我们发现,约束1就是限定区间 [x..y] 必须有 v 这个数,且所有数字小于等于 v 。约束2就是限定区间 [x..y] 必须有 v 这个数,且所有数字大于等于 v 。假设我们求出了位置能取得数的集合,剩下的就是一个二分图匹配的问题了。
那么我们应该仔细考虑一下如何确定每个位置能取哪些数。比赛时我由于太急躁,并没有想好这个条件。
我们归纳总结可以发现,位置 i 能放 j ,当且仅当 j 满足所有包含位置 i 的约束,并且在不包含位置 i 的约束中,不存在 v=j
我们通过枚举约束求出每个位置最大能放几,最小能放几,同时求出每个数是否出现在不包含该位置的约束中。然后位置向能连的数连边,做匈牙利匹配即可。
时间复杂度 O(n3)

代码实现

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>

using namespace std;

const int N=200;
const int M=40000;

struct constraint
{
    int kind,l,r,x;
}c[M+1];

int next[N*N+1],tov[N*N+1];
int belong[N+1],last[N+1];
bool vis[N+1],num[N+1],r[N+1];
int n,m,can,tot;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9')
    {
        if (ch=='-')
            f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}

void insert(int x,int y)
{
    tov[++tot]=y;
    next[tot]=last[x];
    last[x]=tot;
}

bool hungary(int x)
{
    int i=last[x],y;
    vis[x]=true;
    while (i)
    {
        y=tov[i];
        if (!belong[y]||!vis[belong[y]]&&hungary(belong[y]))
        {
            belong[y]=x;
            return true;
        }
        i=next[i];
    }
    return false;
}

bool cmp(constraint x,constraint y)
{
    return x.l<y.l;
}

int main()
{
    freopen("arrangement.in","r",stdin);
    freopen("arrangement.out","w",stdout);
    n=read(),m=read();
    for (int i=1;i<=m;i++)
        c[i].kind=read(),c[i].l=read(),c[i].r=read(),c[i].x=read();
    int mins,maxs;
    for (int i=1;i<=n;i++)
    {
        memset(r,false,sizeof r);
        mins=1,maxs=n;
        for (int j=1;j<=m;j++)
            if (c[j].l<=i&&i<=c[j].r)
                if (c[j].kind==1)
                    maxs=min(maxs,c[j].x);
                else
                    mins=max(mins,c[j].x);
            else
                r[c[j].x]=true;
        mins=max(mins,1);
        maxs=min(maxs,n);
        for (int j=mins;j<=maxs;j++)
            if (!num[j]&&!r[j])
                insert(i,j);
    }
    for (int i=1;i<=n;i++)
    {
        memset(vis,false,sizeof vis);
        if (hungary(i))
            can++;
    }
    if (can<n)
        printf("-1");
    else
    {
        for (int i=1;i<=n;i++)
            last[belong[i]]=i;
        for (int i=1;i<n;i++)
            printf("%d ",last[i]);
        printf("%d\n",last[n]);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

你可能感兴趣的:(网络流,二分图,最大匹配,匈牙利算法)