问题描述
给定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; }