题目描述
终端部门为了对穿戴设备进行交叉测试,目前有 ( n ) 名员工投入测试,人员从 1 到 ( n ) 依次编号。为了充分测试和暴露问题,要求任何两个以前戴过同一穿戴设备的人不能再次测同一设备。下面会给出测试投入的人数和测过同一台设备的人员编号,请按照此关系,计算这次至少需要几台穿戴设备供测试。
输入描述
第一行,一个整数 ( n )(1 < n < 100),表示参加测试的人数,人员从 1 到 ( n ) 依次编号。
第二行,一个整数 ( m ),表示接下来有 ( m ) 行数据,1 <= m <= C(n, 2)。
以下 ( m ) 行每行的格式为:两个整数 ( a ),( b ),分别表示员工 ( a ) 和员工 ( b ),用空格分开(1 <= a, b <= n),表示员工 ( a ) 与员工 ( b ) 以前测过同一穿戴设备。
输出描述
输出为一个整数,表示至少需要几台穿戴设备供测试。
用例输入
5
8
1 2
1 3
1 4
2 3
2 4
2 5
3 4
3 5
4
解题思路
这是一个典型的图着色问题,其中每个员工可以看作图中的一个节点,而两个员工以前测过同一设备的关系可以看作图中的一条边。目标是为每个节点分配一个颜色(即穿戴设备),使得相邻节点(即以前测过同一设备的员工)不能有相同的颜色,并且使用的颜色总数最少。
方法一:贪心算法(基于饱和度优先的着色)
- 图的表示:使用邻接表表示图,其中每个节点存储与其相连的节点集合。
- 初始化:
colors
:存储每个节点的颜色,初始值为 -1(表示未着色)。sat
:存储每个节点的饱和度,即其邻居已使用的颜色数量。degree
:存储每个节点的度,即与其相连的节点数量。
- 选择节点:每次选择未着色且具有最高饱和度的节点进行着色。如果存在多个节点具有相同的饱和度,则选择度数最大的节点。
- 分配颜色:为选定的节点分配最小的未被其邻居使用的颜色。
- 更新饱和度:更新该节点的邻居的饱和度。
- 重复:重复上述过程,直到所有节点都被着色。
- 结果:使用的颜色总数即为所需的穿戴设备数量。
方法二:深度优先搜索(DFS)的回溯算法
- 图的表示:使用邻接矩阵表示图,其中
mp[i][j]
表示节点 ( i ) 和节点 ( j ) 之间是否存在边。 - 初始化:
color
:存储每个节点的颜色,初始值为 -1(表示未着色)。res
:存储当前找到的最小颜色数量。
- DFS搜索:
- 从节点 1 开始,尝试为每个节点分配颜色。
- 对于每个节点,检查其邻居已使用的颜色,选择一个未被使用的最小颜色。
- 如果当前节点无法分配颜色,则尝试分配一个新的颜色。
- 递归地为下一个节点分配颜色。
- 如果所有节点都被成功着色,更新结果。
- 回溯:如果当前分配的颜色数量已经超过当前结果,则回溯,尝试其他颜色分配。
- 结果:最终结果即为所需的穿戴设备数量。
代码
基于饱和度优先的着色 100%
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<queue>
#include<set>
#include<list>
#include<sstream>
#include<bitset>
#include<stack>
#include<climits>
#include<iomanip>
#include<cstdint>
using namespace std;
int n, m;
// 每次选择未着色且具有最高饱和度的顶点进行着色
// 如果存在多个顶点具有相同的最高饱和度,则选择度数最大的顶点。
int choose(vector<int>& colors, vector<int>& sat, vector<int>& degree) {
int max_s = -1;
int max_d = -1;
int v = -1;
for (int i = 0; i < n; i++) {
if (colors[i] == -1) {
if (sat[i] > max_s || (sat[i] == max_s && degree[i] > max_d)) {
max_s = sat[i];
max_d = degree[i];
v = i;
}
}
}
return v;
}
// 未被其邻居使用的最小颜色编号。
int get_color(int v, vector<set<int>>& used) {
int cur = 0;
while (used[v].count(cur)) {
cur++;
}
return cur;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
vector<set<int>> g(n);
for (int i = 0; i < m; i++) {
int x, y;
cin >> x >> y;
x--, y--;
g[x].insert(y);
g[y].insert(x);
}
vector<int> colors(n, -1);
vector<int> sat(n, 0); // 饱和度
vector<int> degree(n, 0); // 度
for (int i = 0; i < n; i++) {
degree[i] = g[i].size();
}
vector<set<int>> used(n);
int maxc = -1;
for (int i = 0; i < n; i++) {
int v = choose(colors, sat, degree);
if (v == -1) {
break;
}
int cur = get_color(v, used);
colors[v] = cur;
maxc = max(maxc, cur);
for (auto& nx : g[v]) {
if (colors[nx] == -1) {
if (!used[nx].count(cur)) {
used[nx].insert(cur);
sat[nx]++;
}
}
}
}
cout << maxc + 1;
}
回溯 50%
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<queue>
#include<set>
#include<list>
#include<sstream>
#include<bitset>
#include<stack>
#include<climits>
#include<iomanip>
#include<cstdint>
using namespace std;
int n, m;
int mp[105][105];
int res = INT_MAX;
int color[105];
// 当前节点 颜色数量
void dfs(int cur, int all) {
if (cur == n + 1) {
res = min(res, all);
return;
}
if (res <= all) return;
int no[105] = { 0 }; // 不能使用的颜色
for (int i = 1; i < cur; i++) {
if (mp[i][cur]) no[color[i]] = 1;
}
for (int i = 0; i <= all; i++) {
if (!no[i]) {
color[cur] = i;
dfs(cur + 1, all);
}
}
color[cur] = all + 1;
dfs(cur + 1, all + 1);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
mp[i][j] = 0;
}
}
cin >> m;
for (int i = 0; i < m; i++) {
int x, y;
cin >> x >> y;
mp[x][y] = mp[y][x] = 1;
}
dfs(1, 0);
cout << res + 1;
}