完美标号

测评传送门

问题描述

给定M个二元组(A[i], B[i]),求X[1], ..., X[N]满足:对于任意(A[i], B[i]),有|X[A[i]] - X[B[i]]| = 1成立。

输入

第1行,2个整数N、M。

第2行到第M + 1行,2个整数A_i和B_i。

输出

第1行,1个字符串,"YES"表示有解,"NO"表示无解。

第2行,N个整数,X_1, X_2, ..., X_N,无解则不输出。

要求|X_i| <= 1,000,000,000,任意一解均可。

样例

Perfect1.in

3 3

1 2

2 3

3 1

 

Perfect1.out

NO

 

Perfect2.in

6 5

1 2

2 3

3 4

4 1

5 6

 

Perfect2.out

YES

0 1 0 1 -99 -100

 

数据范围

对于40%的数据,1 <= N <= 10。

对于100%的数据,1 <= N <= 10,000,0 <= M <= 100,000,1 <= A_i, B_i <= N。


思路:

首先我们可以发现对于所有i对应的x[i],将x[i]填为1和0永远是最优的,因为这两个数最接近最容易构成相差为1的关系(a或者a-1也可以)

然后40%只需要枚举所有情况即可,然后对于m个询问逐个检查即可(其实并没有m个,有很多重复的,最多只有n^2)。时间复杂度O(2^n*n^2)

100% 与关押罪犯类似,用并查集或者二分图染色解决

并查集:对于输入的两个点a,b,我们一定要做路径压缩,然后把a的父亲和b的父亲连接到一起,并且再设一个数组dis[i]表示i到fa[i]的距离%2;如果a和b已经在同一并查集里面了,就要看它们到根节点的距离是否不同,如果相同则说明答案为NO.

二分图染色:对于输入的点和m条边建图之后,从1开始dfs,然后这个点记录为1则下一个点记录为0....这样交替记录即可,如果某一个点已经被标记为1而它要被填为0就输出NO

考试代码

#include<stdio.h>
#include<algorithm>
using namespace std;
const int MX=2e5+1;
int n,m,cnt,x[MX],first[MX],dfn[MX],k;
bool flag=false;
struct Edge {
    int to,next;
}edge[MX]; 

void add(int from,int to)
{
    edge[++cnt].to=to;
    edge[cnt].next=first[from];
    first[from]=cnt;
}

void dfs(int from,int last)
{
    if(!dfn[from]){
        dfn[from]=++k;
        x[from]=~x[last];
    }
        
    else {
        if(abs(x[from]-x[last])!=1) 
        flag=true;
        return ;
    }
        
    for(int i=first[from];i;i=edge[i].next)
    {
        int to=edge[i].to;
        if(to==last) continue;
        dfs(to,from);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        int from,to;
        scanf("%d%d",&from,&to);
        add(from,to);
        add(to,from);
    }
    for(int i=1;i<=n;++i) 
    {
        flag=false;
        if(!dfn[i]) {
            dfs(i,0);
            if(flag) {
                printf("NO");
                return 0;
            }
        }
    }
    puts("YES");
    for(int i=1;i<=n;++i) 
        printf("%d ",x[i]);
    return 0;
}

修改代码

#include<stdio.h>
#include<algorithm>
using namespace std;
const int MX=2e5+1;
int n,m,cnt,x[MX],first[MX];
bool vis[MX];
bool flag=false;
struct Edge {
    int to,next;
}edge[MX]; 

void add(int from,int to)
{
    edge[++cnt].to=to;
    edge[cnt].next=first[from];
    first[from]=cnt;
}

void dfs(int from,int last)
{
    x[from]=1^x[last];
    for(int i=first[from];i;i=edge[i].next)
    {
        int to=edge[i].to;
        if(to==last) continue;
        if(!vis[to]) vis[to]=1;
        else {
            if(abs(x[from]-x[to])!=1) {
                printf("NO");
                exit(0);
            }
            else continue;
        }    
        dfs(to,from);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        int from,to;
        scanf("%d%d",&from,&to);
        add(from,to);
        add(to,from);
    }
    for(int i=1;i<=n;++i) 
    {
        if(!vis[i]) 
            dfs(i,0);
    }
    puts("YES");
    for(int i=1;i<=n;++i) 
        printf("%d ",x[i]);
    return 0;
}
从0到1很难,但从1到100很容易
原文地址:https://www.cnblogs.com/qseer/p/9524285.html