目录
题目描述
输入描述
输出描述
用例
题目解析
算法源码
给一个无向图染色,可以填红黑两种颜色,必须保证相邻两个节点不能同时为红色,输出有多少种不同的染色方案?
第一行输入M(图中节点数) N(边数)
后续N行格式为:V1 V2表示一个V1到V2的边。
数据范围:1 <= M <= 15,0 <= N <= M * 3,不能保证所有节点都是连通的。
输出一个数字表示染色方案的个数。
| 输入 | 4 4 1 2 2 4 3 4 1 3 |
| 输出 | 7 |
| 说明 | 4个节点,4条边,1号节点和2号节点相连, 2号节点和4号节点相连,3号节点和4号节点相连, 1号节点和3号节点相连, 若想必须保证相邻两个节点不能同时为红色,总共7种方案。 |
用例的染色方案,如下示意图:
相邻节点不能同时为红色

求一个连通图的染色方案,策略如下:
首先,必然有一种全黑方案。

然后,我们找第一个点,即点1,找它的不相邻点,只有一个4,因此我们可以选择只给1染色,或者只给4染色,或者给1,4同时染色,这三个方案都符合要求

其实,这就是求1,4的全组合个数,即求 C(n,1) + C(n,2) + C(n,3) + ...... + C(n,n-1) + C(n,n) = (2^n) - 1
比如1,4有两个数,即一共有 (2^2) - 1 = 3 种。
按照上面这种策略,
继续找第二个点,即点2,其不相邻的只有点3,因此一共有(2^2) - 1 = 3 种染色方案
继续找第三个点,即点3,其不相邻的只有点2,因此一共有(2^2) - 1 = 3 种染色方案
继续找第四个点,即点4,其不相邻的只有点1,因此一共有(2^2) - 1 = 3 种染色方案
此时,我们发现除了全黑的染色方案外,一共有3*4 = 12种染色方案,但是实际上只有6种

整整多了一倍。
原因很简单,我们在统计第一个点的染色方案时,其实把第四个点的也统计了。统计第二个点时,把第三个点的染色方案也统计了。
因此后面对于第三个点和第四个点染色方案统计是重复的。
因此,可得连通图的染色方案数目统计代码如下:
入参arr,即图中所有边 [v1, v2],
入参m,即图中点的数量
// 连通图的染色方案
function getDyeCount(arr, m) {const graph = {};for (let [v1, v2] of arr) {graph[v1] ? graph[v1].push(v2) : (graph[v1] = [v2]);graph[v2] ? graph[v2].push(v1) : (graph[v2] = [v1]);}let count = 0;for (let v in graph) {const n = m - graph[v].length;count += (1 << n) - 1; // (2 ^ n) - 1}return (count >>> 1) + 1; // +1 对应全黑染色方案,count >>> 1 即 count / 2
}
本题,到此还未结束,因为题目中有一句话:
不能保证所有节点都是连通的
这说明什么呢?即,可能会出现下面这种情况:

那么此时染色方案该如何统计呢?
题目和用例都没有说明清楚。。。。
我理解,应该统计每个连通分量的染色方案个数,然后求和作为题解。
即先统计1,2,3,4连通分量的染色方案个数为7,再统计5连通分量的染色方案为2,即一共有9个染色方案。
因此,本题还考察了连通分量的求解。
连通分量的求解可以使用并查集,关于并查集知识请看:华为机试 - 发广播_伏城之外的博客-CSDN博客
/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");const rl = readline.createInterface({input: process.stdin,output: process.stdout,
});const lines = [];
let m, n;
rl.on("line", (line) => {lines.push(line);if (lines.length === 1) {[m, n] = lines[0].split(" ").map(Number);}if (n !== undefined && lines.length === n + 1) {const arr = lines.slice(1).map((line) => line.split(" ").map(Number));console.log(getResult(arr, m));lines.length = 0;}
});/**** @param {*} arr 边,即[v1, v2]* @param {*} m 点数量*/
function getResult(arr, m) {const ufs = new UnionFindSet(m);for (let [v1, v2] of arr) {ufs.union(v1, v2);}const cates = {}; // cates对象每个属性对应一个连通分量for (let i = 1; i < ufs.fa.length; i++) {let fa = (ufs.fa[i] = ufs.find(i));cates[fa] ? cates[fa].push(i) : (cates[fa] = [i]);}let count = 0;for (let k in cates) {// 连通分量中点的个数const len = cates[k].length;const set = new Set(cates[k]);// 如果输入1~n+1行的边中还有连通分量中的点,则将边计入同一个连通图connect中const connected = arr.filter(([v1, v2]) => set.has(v1) || set.has(v2));// 求连通图的染色方案个数count += getDyeCount(connected, len);}return count;
}// 并查集
class UnionFindSet {constructor(n) {this.fa = new Array(n + 1).fill(0).map((_, i) => i);}find(x) {if (x !== this.fa[x]) {return (this.fa[x] = this.find(this.fa[x]));}return x;}union(x, y) {const x_fa = this.find(x);const y_fa = this.find(y);if (x_fa !== y_fa) {this.fa[y_fa] = x_fa;}}
}// 连通图的染色方案
function getDyeCount(arr, m) {const graph = {};for (let [v1, v2] of arr) {graph[v1] ? graph[v1].push(v2) : (graph[v1] = [v2]);graph[v2] ? graph[v2].push(v1) : (graph[v2] = [v1]);}let count = 0;for (let v in graph) {const n = m - graph[v].length;count += (1 << n) - 1; // (2 ^ n) - 1}return (count >>> 1) + 1; // +1 对应全黑染色方案,count >>> 1 即 count / 2
}
上一篇:消费纠纷案维权主体趋于年轻化