Acwin 第六场周赛 第三题 构造完全图

题目链接 :
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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值