题目链接 :
3735. 构造完全图
题目描述 :
题目思路 :
首先明确当 m == (n * (n + 1)) / 2 时 , 原图即为一个完全图,无需进行操作
看数据范围,点的范围最大 22,则可以想到用状态压缩来解决该问题
然后翻译题目模型,即 :
每次选择一个点相当于 {与该点相连的点} 与 该点 成为一个 团(完全图)。
那么对于每一个完全图的状态,我们都可以选择该状态中的一个点来进行扩展的操作
那么就是对于每一种状态的遍历和每一个点的遍历即可
时间复杂度就是 : O(n*(1 << n))
int e[25];// e[i] 表示与 i 相连的点的集合
int f[N];// f[i] 到状态为 i 的情况的最小操作次数
PII g[N];// g[i] = {a,b}; 到状态 i 的转移路径是通过点 b 扩展 状态 a 得到
for(int i = 0;i < (1 << n);i ++) {
// 对状态的遍历
for(int j = 0;j < n;j ++) {
// 对每一个点的遍历
if(i >> j & 1) {
int S = i | e[j];
if(f[S] > f[i] + 1) {
f[S] = f[i] + 1;
g[S] = {i,j + 1};
}
}
}
}
那么初始化时就是 : 将每一个点与之相连的点的作为初始的一个团,并标记到该点的最小操作数为 1 , 状态转移为 {0,i} 即 , 从 0 状态 通过 i 这个点进行扩展得到
memset(e,0,sizeof e);
for(int i = 0;i < n;i ++) e[i] = (1 << i);
for(int i = 0;i < m;i ++) {
int a,b;
scanf("%d%d", &a, &b);
a --;
b --;
// 改为 0 为索引
e[a] |= (1 << b);
e[b] |= (1 << a);
}
memset(f,30,sizeof(f));// 标记为最大值
for(int i = 0;i < n;i ++) {
f[e[i]] = 1;
g[e[i]] = {0,i + 1};
// 是从 i 这个点转移过来的
}
此时不需要考虑某一个点本来就是一个团,而我们设置通过该点成团的最小操作为 1 的情况
因为,最后的最优解一定会从其他的一个点来将该团吞并,从而忽略这个问题。
代码 :
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
using LL = long long;
using PII = pair<int,int>;
const int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
int n,m,a,b;
const int N = 5e6 + 10;
int e[25];// e[i] 表示与 i 相连的点的集合
int f[N];// f[i] 到状态为 i 的情况的最小操作次数
PII g[N];// g[i] = {a,b}; 到状态 i 的转移路径是通过点 b 扩展 状态 a 得到
int main()
{
cin >> n >> m;
if(m == (n * (n-1)) / 2) {
cout<<0<<endl;// 本来就是一个完全图
return 0;
}
memset(e,0,sizeof e);
for(int i = 0;i < n;i ++) e[i] = (1 << i);
for(int i = 0;i < m;i ++) {
int a,b;
scanf("%d%d", &a, &b);
a --;
b --;
// 改为 0 为索引
e[a] |= (1 << b);
e[b] |= (1 << a);
}
memset(f,30,sizeof(f));// 标记为最大值
for(int i = 0;i < n;i ++) {
f[e[i]] = 1;
g[e[i]] = {0,i + 1};
// 是从 i 这个点转移过来的
}
for(int i = 0;i < (1 << n);i ++) {
for(int j = 0;j < n;j ++) {
if(i >> j & 1) {
int S = i | e[j];
if(f[S] > f[i] + 1) {
f[S] = f[i] + 1;
g[S] = {i,j + 1};
}
}
}
}
int k = (1 << n) - 1;
cout<<f[k]<<endl;
while(k) {
cout<<g[k].second<<" ";
k = g[k].first;
}
cout<<endl;
return 0;
}