华为OD机试真题-红黑图
华为OD机试双机位C卷 - 华为OD上机考试双机位C卷 100分题型
华为OD机试双机位C卷真题目录点击查看: 华为OD机试双机位C卷真题题库目录|机考题库 + 算法考点详解
题目描述
总所周知红黑树时一种平衡树,它最突出的特性就是不能有两个相连的红色节点。那我们定义一个红黑图,也就是一张无向图中,每个节点可能是红黑两种颜色,但我们保证没有两个相邻的红色节点。
现在给一张未染色的无向图,只能染红黑两种颜色,问总共有多少种染色方案使得它成为一个红黑图。
输入描述
第一行输入M(图中节点数) N(边数)
后续N行格式为:V1 V2表示一个V1到V2的边。
数据范围:1 <= M <= 15,0 <= N <= M * 3,不能保证所有节点都是连通的。保证没有重边和自环。
输出描述
输出一个数字表示染色方案的个数。
用例1
输入
none
4 4
1 2
2 4
3 4
1 3
输出
none
7
说明
4个节点,4条边,1号节点和2号节点相连,
2号节点和4号节点相连,3号节点和4号节点相连,
1号节点和3号节点相连,
若想必须保证相邻两个节点不能同时为红色,总共7种方案。
用例2
输入
none
3 3
1 2
1 3
2 3
输出
none
4
用例3
输入
none
4 3
1 2
2 3
3 4
输出
none
8
用例4
输入
none
4 3
1 2
1 3
2 3
输出
none
8
题解
思路:暴力枚举,题目数据量比较少直接使用m <= 15直接顶点所有颜色状态分配状态然后判断是否合法。
- 通过顶点数量确定
枚举范围 [0 ,2^m - 1] - 枚举的值代表一种分配方案,举个例子假设当前
m = 3 枚举值为3 对应二进制位 011,我们设定二进制为1代表分配为红色,0代表分配为白色。 - 对于每个枚举值,合法性判断就是判断当前方案是否将相邻边同时分配为红色。假设有一个边对应两个顶点分别为
x y,枚举值为i,对应判断表达式为(i >> (x-1) & 1) == 1 && (i >> (y-1) & 1) == 1. 如果当前所有边的合法性判断都通过则结果 + 1. - 输出枚举完所有方案的结果。
c++
c++
#include<iostream>
#include<vector>
#include<string>
#include <utility>
#include <sstream>
#include<algorithm>
#include<cmath>
using namespace std;
int main() {
int n,m;
// 节点 边
cin >> m >> n;
vector<pair<int, int>> edges(n);
for (int i = 0; i < n; i++) {
int x , y;
cin >> x >> y;
edges[i] = {x, y};
}
// 没有边所以说明每个节点都能取红或黑
if (n == 0) {
cout << pow(2, m);
return 0;
}
int res = 0;
// 保留枚举所有情况判断是否合法 二进制方式枚举 二进制对应位 1 代表取红 0 代表为 黑
for (int i = 0; i < (int)pow(2,m); i++) {
// 判断是否合法
bool flag = true;
for (int j = 0; j < n; j++) {
int x = edges[j].first;
int y = edges[j].second;
// 是否相邻边同为红
if ((i >> (x-1) & 1) == 1 && (i >> (y-1) & 1) == 1) {
flag = false;
break;
}
}
if (flag) {
res++;
}
}
cout << res;
return 0;
}
JAVA
JAVA
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 节点数 m,边数 n
int m = sc.nextInt();
int n = sc.nextInt();
int[][] edges = new int[n][2];
for (int i = 0; i < n; i++) {
edges[i][0] = sc.nextInt();
edges[i][1] = sc.nextInt();
}
// 没有边,说明每个节点可以独立染色为红或黑
if (n == 0) {
System.out.println((int)Math.pow(2, m));
return;
}
int res = 0;
// 枚举所有染色情况,用二进制方式表示染色状态
for (int i = 0; i < (1 << m); i++) {
boolean flag = true;
for (int[] edge : edges) {
int x = edge[0], y = edge[1];
// 相邻节点不能同时为红色(即都为1)
if (((i >> (x - 1)) & 1) == 1 && ((i >> (y - 1)) & 1) == 1) {
flag = false;
break;
}
}
if (flag) res++;
}
System.out.println(res);
}
}
Python
python
# 节点数 m,边数 n
m, n = map(int, input().split())
edges = [tuple(map(int, input().split())) for _ in range(n)]
# 没有边,每个节点可红或黑,共2^m种方案
if n == 0:
print(2 ** m)
exit()
res = 0
# 枚举所有染色情况,1表示红色,0表示黑色
for i in range(1 << m):
valid = True
for x, y in edges:
if (i >> (x - 1)) & 1 and (i >> (y - 1)) & 1:
valid = False
break
if valid:
res += 1
print(res)
JavaScript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let inputLines = [];
rl.on('line', (line) => {
inputLines.push(line);
}).on('close', () => {
const [m, n] = inputLines[0].split(' ').map(Number); // 读取节点数和边数
const edges = inputLines.slice(1).map(line => line.split(' ').map(Number));
if (n === 0) {
console.log(2 ** m); // 没有边,2^m 种方案
return;
}
let res = 0;
// 用二进制枚举所有染色方案
for (let i = 0; i < (1 << m); i++) {
let valid = true;
for (const [x, y] of edges) {
// 判断相邻节点是否都为红色(用1表示红色)
if (((i >> (x - 1)) & 1) && ((i >> (y - 1)) & 1)) {
valid = false;
break;
}
}
if (valid) res++;
}
console.log(res);
});
Go
go
package main
import (
"fmt"
)
func main() {
var m, n int
fmt.Scan(&m, &n)
edges := make([][2]int, n)
for i := 0; i < n; i++ {
fmt.Scan(&edges[i][0], &edges[i][1])
}
// 没有边,每个节点可以红或黑,总共 2^m 种方案
if n == 0 {
fmt.Println(1 << m)
return
}
res := 0
// 枚举所有染色方案,1 表示红色
for i := 0; i < (1 << m); i++ {
valid := true
for _, edge := range edges {
x, y := edge[0], edge[1]
if ((i>>(x-1))&1) == 1 && ((i>>(y-1))&1) == 1 {
valid = false
break
}
}
if valid {
res++
}
}
fmt.Println(res)
}
C语言
cpp
#include <stdio.h>
#include <math.h>
#define MAXN 100
// 定义边结构体
typedef struct {
int x;
int y;
} Edge;
int main() {
int n, m;
// 节点 边
scanf("%d %d", &m, &n);
Edge edges[MAXN];
for (int i = 0; i < n; i++) {
scanf("%d %d", &edges[i].x, &edges[i].y);
}
// 没有边所以说明每个节点都能取红或黑
if (n == 0) {
// pow返回double,这里强转为int
printf("%d", (int)pow(2, m));
return 0;
}
int res = 0;
// 保留枚举所有情况判断是否合法
// 二进制方式枚举 二进制对应位 1 代表取红 0 代表为黑
int total = (int)pow(2, m);
for (int i = 0; i < total; i++) {
// 判断是否合法
int flag = 1;
for (int j = 0; j < n; j++) {
int x = edges[j].x;
int y = edges[j].y;
// 是否相邻边同为红
if (((i >> (x - 1)) & 1) == 1 && ((i >> (y - 1)) & 1) == 1) {
flag = 0;
break;
}
}
if (flag) {
res++;
}
}
printf("%d", res);
return 0;
}