本专栏持续输出数据结构题目集,欢迎订阅。
题目
顶点覆盖是图中一些顶点的集合,使得图中的每条边都至少以该集合中的一个顶点为端点。“最优顶点覆盖问题”是求顶点数最少的顶点覆盖。
输入格式:
输入第一行给出正整数 n(≤10)和 m,分别对应图中顶点和边的数量。
随后 m 行,每行给出一条边的一对顶点的编号,其间以空格分隔。顶点从 0 到 n−1 编号。
最后一行给出模拟退火需要的参数,依次为:初始温度 T、降温系数 α、概率参数 k、迭代降温次数 iter_num、温度阈值 ϵ。
输出格式:
输出应用模拟退火算法得到的顶点覆盖集合中顶点的编号。为简单起见,每个编号后面跟 1 个空格。
注意:解可能不唯一,输出任意一组即可。
输入样例:
7 6
0 1
1 2
2 3
3 4
4 5
5 6
1000 0.2 2 20 5
输出样例:
1 3 5
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#define MAX_N 10
// 边的结构体
typedef struct {
int u, v;
} Edge;
// 计算解的代价(顶点数量)
int cost(int* solution, int n) {
int cnt = 0;
for (int i = 0; i < n; i++) {
if (solution[i]) cnt++;
}
return cnt;
}
// 检查解是否为有效的顶点覆盖
int is_valid(int* solution, int n, Edge* edges, int m) {
for (int i = 0; i < m; i++) {
int u = edges[i].u;
int v = edges[i].v;
if (solution[u] == 0 && solution[v] == 0) {
return 0; // 存在未被覆盖的边
}
}
return 1; // 有效顶点覆盖
}
// 生成随机初始解
void generate_initial_solution(int* solution, int n, Edge* edges, int m) {
// 先从空集开始
memset(solution, 0, sizeof(int) * n);
// 确保初始解是有效的顶点覆盖
while (!is_valid(solution, n, edges, m)) {
// 随机选择一个未被覆盖的边,将其一个顶点加入覆盖
int idx;
do {
idx = rand() % m;
} while (solution[edges[idx].u] || solution[edges[idx].v]);
// 随机选择边的一个顶点加入覆盖
if (rand() % 2 == 0) {
solution[edges[idx].u] = 1;
} else {
solution[edges[idx].v] = 1;
}
}
}
// 生成邻域解(随机翻转一个顶点的状态)
void generate_neighbor(int* current, int* neighbor, int n) {
memcpy(neighbor, current, sizeof(int) * n);
int idx = rand() % n;
neighbor[idx] = 1 - neighbor[idx]; // 翻转状态
}
// 模拟退火算法求解顶点覆盖
void simulated_annealing(int n, int m, Edge* edges,
double T, double alpha, double k, int iter_num, double epsilon,
int* best_solution) {
int current[MAX_N];
int neighbor[MAX_N];
int best[MAX_N];
// 生成初始解
generate_initial_solution(current, n, edges, m);
memcpy(best, current, sizeof(int) * n);
int current_cost = cost(current, n);
int best_cost = current_cost;
// 模拟退火主循环
while (T > epsilon) {
// 在当前温度下迭代一定次数
for (int i = 0; i < iter_num; i++) {
// 生成邻域解
generate_neighbor(current, neighbor, n);
// 只考虑有效的顶点覆盖
if (!is_valid(neighbor, n, edges, m)) {
continue;
}
// 计算邻域解的代价
int neighbor_cost = cost(neighbor, n);
int delta = neighbor_cost - current_cost;
// 如果邻域解更优,接受它
if (delta < 0) {
memcpy(current, neighbor, sizeof(int) * n);
current_cost = neighbor_cost;
// 更新最优解
if (current_cost < best_cost) {
memcpy(best, current, sizeof(int) * n);
best_cost = current_cost;
}
} else {
// 以一定概率接受较差的解
double prob = exp(-k * delta / T);
double r = (double)rand() / RAND_MAX;
if (r < prob) {
memcpy(current, neighbor, sizeof(int) * n);
current_cost = neighbor_cost;
}
}
}
// 降温
T *= alpha;
}
memcpy(best_solution, best, sizeof(int) * n);
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
Edge edges[m];
for (int i = 0; i < m; i++) {
scanf("%d %d", &edges[i].u, &edges[i].v);
}
// 读取模拟退火参数
double T, alpha, k, epsilon;
int iter_num;
scanf("%lf %lf %lf %d %lf", &T, &alpha, &k, &iter_num, &epsilon);
// 初始化随机数生成器
srand(time(0));
// 求解顶点覆盖
int best_solution[MAX_N];
simulated_annealing(n, m, edges, T, alpha, k, iter_num, epsilon, best_solution);
// 输出结果
for (int i = 0; i < n; i++) {
if (best_solution[i]) {
printf("%d ", i);
}
}
printf("\n");
return 0;
}
3117

被折叠的 条评论
为什么被折叠?



