P1330 封锁阳光大学

题目传送门

一、二分图着色原理

二、dfs深度优先遍历解法

#include <bits/stdc++.h>

using namespace std;

//dfs代码简短一些,注意第29行。
const int N = 1e4 + 10;     //题目中说结点数最大10^4=1e4
const int M = 1e5 * 2 + 10; //边数上限,因为是无向图,所以记录两次,就是1e5*2

//链式前向星
struct Edge {
    int to, next;
} e[M];
int head[N], idx;

void add(int u, int v) {
    e[++idx].to = v;
    e[idx].next = head[u];
    head[u] = idx;
}

int n, m, ans, ans1, ans2;
int color[N];     //上色结果

/**
 * 功能:对二分图进行上色
 * @param u 要上色的结点
 * @param c 上一个过来结点的颜色
 */
void dfs(int u, int c) {
    //上一个结点颜色是1,那么u就是2
    if (c == 1) color[u] = 2, ans2++;
        //上一个结点颜色是2,那么u就是1
    else if (c == 2) color[u] = 1, ans1++;
    //遍历每个出边
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].to;
        //如果已上色
        if (color[v] > 0) {
            //颜色与上一个结点的颜色一样,那么就是冲突
            if (color[v] == color[u]) {
                printf("Impossible");
                exit(0);
            }
        }//未上色,则进行上色即可
        else dfs(v, color[u]);
    }
}

int main() {
    cin >> n >> m;
    int x, y;
    for (int i = 1; i <= m; ++i) {
        cin >> x >> y;
        add(x, y), add(y, x);//无向图,双向创建
    }
    //寻找每个结点,如果没有染过颜色,就深度优先开始染色
    for (int i = 1; i <= n; ++i)
        if (!color[i]) {
            //每一轮用之前清空为零
            ans1 = ans2 = 0;
            dfs(i, 1);//假设第一个结点是颜色1
            //两种颜色哪个少哪个是答案
            ans += min(ans1, ans2);
        }
    //如果有解,那么输出
    printf("%d", ans);
    return 0;
}

三、bfs广度优先遍历解法

#include <bits/stdc++.h>

using namespace std;

const int N = 1e4 + 10; //题目中说结点数最大10^4=1e4
const int M = 1e5 * 2 + 10; //边数上限,因为是无向图,所以记录两次,就是1e5*2
/*
根据题意可知二分图染色。若无法被染成二分图,输出Impossible;
反之,对于每个二分图,记录两种颜色的点的个数,取min后记录答案中。

注意,图可能不连通。因此对于每个二分图,都要进行取min操作,而不是对整个图染色后取min。
*/

int color[N]; //0:未上色,1:黑色,2:白色
int n, m, ans;

//广度优先搜索的队列
queue<int> q;

//链式前向星
struct Edge {
    int to, next;
} edge[M];

int idx, head[N];

void add(int u, int v) {
    edge[++idx].to = v;
    edge[idx].next = head[u];
    head[u] = idx;
}

//广度优先搜索
int bfs(int a) {
    int ans1 = 0, ans2 = 0;
    color[a] = 1;
    ans1++;
    q.push(a);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].to;
            //目标点未着色
            if (color[v] == 0) {
                //来源点是黑的,那么目标点是白的
                if (color[u] == 1) color[v] = 2, ans2++;
                //来源点是白的,那么目标点是黑的
                else if (color[u] == 2) color[v] = 1, ans1++;
                q.push(v);
            } else
                //如果已着色,那么需要判断和现在的要求是不是有冲突:
                if (color[v] == color[u]) {//相临的存在冲突
                printf("Impossible");
                exit(0);
            }
        }
    }
    //哪个小就用哪个
    return min(ans1, ans2);
}

int main() {
    //n个结点,m条边
    cin >> n >> m;
    //m条边
    for (int i = 1; i <= m; ++i) {
        int x, y;
        cin >> x >> y;
        add(x, y), add(y, x); //无向图双向建边,题目中明显说是无向图!
    }
    //注意,图可能不连通。因此对于每个二分图,都要进行取min操作,
    //而不是对整个图染色后取min。
    //这里有一个小技巧,就是遍历每个结点,如果此结点没有上过色,就表示是一个独立的图,需要单独计算
    for (int i = 1; i <= n; ++i)
        if (!color[i]) ans += bfs(i);

    //输出
    printf("%d", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/littlehb/p/15137826.html