[GDOI模拟2015.08.08]排列

题目大意

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

1n200,0m40000


题目分析

有“约束条件“,再看到n200以内,那这题应该最有可能为网络流。
我们发现,约束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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值