Split Into Two Sets CodeForces - 1702E _二分图_染色法,并查集

Split Into Two Sets CodeForces - 1702E

Polycarp 有一副有 n 张牌的(数字 n 为偶数) 多米诺骨牌. 每张多米诺骨牌上写有两个在 1 到 n 之间的数字.
请问能否把骨牌分成两副,使得每一副骨牌中都没有重复的数字?
所有骨牌都必须被分到其中一副中,且两副骨牌的数量相同。
例如, 假如他有 4 张多米诺骨牌: {1,4}, {1,3}, {3,2} 和 {4,2},
可以将第 1,3 张骨牌分给第一副 ({1,4}和 {3,2}),然后第 2,4 张分给第二副({1,3}和 {4,2})。
提示:可能出现 {1,2}, {2,3}, {3,1}, {4,1} 这样的 4 张骨牌,显然这样的一副骨牌无法按条件分成两副

Input

第一行包含一个整数 t(1≤t≤1e4) — 测试用例的数量。
测试用例的描述如下。
每个测试用例的第一行包含一个偶数 n(2≤n≤2e5) — 多米诺骨牌的数量。
下一个 n 行包含成对的数字 ai,bi — 描述多米诺骨牌上的数字。
保证所有测试用例中 n 的总和不超过 2e5。

Output

对于每个测试用例打印:
如果可以将 n 个多米诺骨牌分为两组,以便每组多米诺骨板上的数字不同,输出 "YES";否则输出 "NO"。

Sample Input

6
4
1 2
4 3
2 1
3 4
6
1 2
4 5
1 3
4 6
2 3
5 6
2
1 1
2 2
2
1 2
2 1
8
2 1
1 2
4 3
4 3
5 6
5 7
8 6
7 8
8
1 2
2 1
4 3
5 3
5 4
6 7
8 6
7 8

Sample Output

YES
NO
NO
YES
YES
NO

分析

考察内容:二分图的判定

二分图:是指可以将图中点集分为相连的两部分,且每部分内部互不相连。

判断一个图是否为二分图,等价于判断图中是否有长度为奇数的环,等价于图是否可以被二染色。
如果没有奇环,则图为二分图。

图染色问题:问是否可以给每个点染上一种颜色,对于所有边来说,边上的两个点颜色均不同。
先该点染红,则与之相连的各个邻居都染蓝,邻居的邻居继续染红,直到所有点都染完,就是一个二分图;如果出现染色冲突,则不是二分图。

  • 方法1:染色法
    思路:将每张牌 {1,4} 看成一个点,连接有共同数据的点,再判断二染色。
    如果集合A中有 {u,v},则可以确定 {u,x}、{v,x} 一定是集合B中的,所以本题可以直接利用每张牌 {u,v} 作边建图,进行二染色。

  • 方法2:并查集
    有相同数据的牌肯定不是在同一集合,如:{1,4} -- {4,2}。
    将每张牌 {1,4} 看成一个点,连接有共同数据的点,每个点恰好连接两条边,也就是每个点的度数都是 2。
    如果一张图中每个点的度数都是 2,可以确定该图是由一些不相交的环组成的。
    用并查集将每一个数对 {u,v} 中的 (u,v) 放在一个连通块中,那么不成立的情况就分为:

  1. 数对 {u,v} 中存在 u==v;
  2. 如果某一个数出现两次以上;
  3. 连通块中的个数是奇数。
  • code 1:染色法 + vector 建图,已 AC,需要注意的地方:
  1. 多组数据的初始化,t≤1e4,n≤2e5,范围较大;
  2. 剪枝优化,有答案就可以直接退出了;
  3. 一个数据出现次数不能大于 2。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e5+10,INF=0x3f3f3f3f;
vector<int> G[N],color(N),vis(N);
int is_two_colorable=1;

void dfs(int u,int c) {
    if(!is_two_colorable) return;
    for(int v : G[u]) {
        int nc=3-c;    // u 的邻居 v 应该染上的色 nc
        if(color[v]) {         // 邻居染过色
            if(color[v]!=nc) { // 发生冲突
                is_two_colorable=0; return;
            }
        } else { // 给它染色
            color[v] = nc, dfs(v, color[v]);
        }
    }
}
int main() {
    //  freopen("data.in", "r", stdin);
    int t,n,u,v;  scanf("%d", &t);
    while(~scanf("%d", &n)) {
        for(int i=0; i<=n; i++) G[i].clear(),color[i]=vis[i]=0;
        is_two_colorable=1;
        for(int i=1; i<=n; i++) {
            scanf("%d%d", &u,&v);
            G[u].push_back(v), G[v].push_back(u);
            vis[u]++, vis[v]++;
            if(vis[u]>2 || vis[v]>2) is_two_colorable=0;
        }
        for(u=1; u<=n; u++) {
            if(is_two_colorable && !color[u]) { // u 还未染色
                color[u]=1, dfs(u, color[u]);
            }
        }
        printf("%s\n", is_two_colorable ? "YES" : "NO");
    }
    return 0;
}
  • code 2: 染色法 + 链式前向星,已 AC
    同样还是需要注意初始化到 n,而不是 N。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e6,INF=0x3f3f3f3f;
int is_two_colorable=1;
struct T {
    int to,next;
} G[N];
int color[N],vis[N],head[N],cnt=0;
void add(int u,int v) {
    G[++cnt].to = v;
    G[cnt].next = head[u];
    head[u] = cnt;
}
void dfs(int u,int c) {
    if(!is_two_colorable) return;
    for(int i=head[u]; ~i; i=G[i].next) {
        int v=G[i].to,nc=3-c; // u 的邻居 v 应该染上的色 nc
        if(color[v]) {        // 邻居染过色
            if(color[v]!=nc) { // 发生冲突
                is_two_colorable=0; return;
            }
        }else{ // 给它染色
            color[v] = nc, dfs(v, color[v]);
        }
    }
}
int main() {
//    freopen("data.in", "r", stdin);
    int t,n,u,v; scanf("%d", &t);
    while(~scanf("%d", &n)) {
        for(int i=0; i<=n; i++) {
            G[i].to=G[i].next=0;
            color[i]=vis[i]=0,head[i]=-1;
        }
        is_two_colorable=1, cnt=0;
        for(int i=1; i<=n; i++) {
            scanf("%d%d", &u,&v),
            add(u,v),add(v,u),vis[u]++,vis[v]++;
            if(vis[u]>2 ||vis[v]>2) is_two_colorable=0;
        }
        for(u=1; u<=n; u++) {
            if(is_two_colorable && !color[u]) { // u 还未染色
                color[u]=1, dfs(u, color[u]);
            }
        }
        printf("%s\n", is_two_colorable ? "YES" : "NO");
    }
    return 0;
}
  • code3 :并查集
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e6+10, INF=0x3f3f3f3f;
int f[N],cnt[N],vis[N];
int find(int x){
    return f[x]==x?x:f[x]=find(f[x]);
}
int main(){
//    freopen("data.in", "r", stdin);
    int t,n,u,v,flag=1; scanf("%d", &t);
    while(~scanf("%d", &n)){
        for(int i=1; i<=n; i++) f[i]=i,cnt[i]=1,vis[i]=0;
        flag=1;
        for(int i=1; i<=n; i++) {
            scanf("%d%d", &u,&v),vis[u]++,vis[v]++;
            if(u==v) flag=0;
            int fu=find(u),fv=find(v);
            if(fu!=fv){
                f[fu]=fv,cnt[fv]+=cnt[fu];
            }
        }
        for(int i=1; i<=n; i++){
            if(flag==0||cnt[find(i)]&1||vis[i]>2){
                flag=0; break;
            }
        }
        printf("%s\n", flag ? "YES":"NO");
    }
    return 0;
}
posted @   HelloHeBin  阅读(207)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示