JZOJ 3163. 【GDOI2013模拟2】排列
题目
Description
给你 M M M个对 1 1 1到 N N N的排列的特征,特征有两种:
1 1 1 x x x y y y v v v:排列的第 x x x个数到第 y y y个数之间的最大值为 v v v;
2 2 2 x x x y y y v v v:排列的第 x x x个数到第 y y y个数之间的最小值为 v v v.
要求你还原出这个排列。
Input
第一行输入两个整数 N ( 1 ≤ N ≤ 200 ) N(1≤N≤200) N(1≤N≤200), M ( 0 ≤ M ≤ 40 , 000 ) M(0≤M≤40,000) M(0≤M≤40,000), N N N表示排列的数字个数, M M M表示特征数量。
接下来 M M M行,每行描述一个特征。
Output
输出一行,满足所有特征的 1 1 1到 N N N的一个排列。方案可能有很多,输出其中一个即可。如果无解,输出 − 1 -1 −1。
Sample Input
输入1:
3 2
1 1 1 1
2 2 2 2
输入2:
4 2
1 1 1 1
2 3 4 1
输入3:
5 2
1 2 3 3
2 4 5 4
Sample Output
输出1:
1 2 3
输出2:
-1
输出3:
1 2 3 4 5
Data Constraint
100%的数据满足 N ( 1 ≤ N ≤ 200 ) , M ( 0 ≤ M ≤ 40 , 000 ) N(1≤N≤200),M(0≤M≤40,000) N(1≤N≤200),M(0≤M≤40,000).
题解
- 由题目可知,最终的序列是 1 1 1到 N N N的排列,数字没有重复,不难想到二分图匹配!
- 怎么连边?
- 根据题目的要求,可以得到每个位置的数字的上下界,全部连上一条边(最初的上下界为 [ 1 , N ] [1,N] [1,N])。
- 但是,即使每个位置上的数都符合上下界的要求,但也不一定满足 M M M个区间的最大/最小值的要求。
- 为什么?
- 某区间中的数,假若它们上下界均为 [ l , r ] [l,r] [l,r],且要求该区间最小值为 l l l,
- 但如果直接跑一遍匈牙利算法,得到的这个区间所选的数不一定包含 l l l(因为还有其它选择),
- 以至于这个区间的最小值不是要求的 l l l,
- 所以,光判断上下界是不行的!!!
- 怎么修改?
- 很简单,若某区间的最大/最小值为 x x x,则必须保证这个区间中的某个数向 x x x连了一条边,否则不符合题意,
- 为了保证这样,就把该区间外的位置向 x x x连的边全部删除。
- 如此以来,如果最终能跑出来,那么一定是符合每个区间的要求的。
代码
#include<cstdio>
#include<cstring>
using namespace std;
struct
{
int a,l,r,s;
}a[40010];
int bz[210],c[210],d[210];
int e[210][210],n;
int dfs(int k)
{
for(int i=1;i<=n;i++) if(e[k][i]&&!bz[i])
{
bz[i]=1;
if(d[i]==0||dfs(d[i]))
{
d[i]=k;
c[k]=i;
return 1;
}
}
return 0;
}
int main()
{
int m,i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++) scanf("%d%d%d%d",&a[i].a,&a[i].l,&a[i].r,&a[i].s);
for(i=1;i<=n;i++)
{
int l=1,r=n;
for(j=1;j<=m;j++)
{
if(a[j].a==1&&i>=a[j].l&&i<=a[j].r&&a[j].s<r) r=a[j].s;
if(a[j].a==2&&i>=a[j].l&&i<=a[j].r&&a[j].s>l) l=a[j].s;
}
for(j=l;j<=r;j++) e[i][j]=1;
}
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++) if(j<a[i].l||j>a[i].r) e[j][a[i].s]=0;
}
int s=0;
for(i=1;i<=n;i++)
{
memset(bz,0,sizeof(bz));
if(dfs(i)) s++;
}
if(s<n)
{
printf("-1");
return 0;
}
for(i=1;i<=n;i++) printf("%d ",c[i]);
return 0;
}