Atcoder ABC 187 F - Close Group 题解

文章描述了一个关于图论的问题,给定一个最多18个节点的简单无向图,需要通过删除一些边,使得任何两个连通的节点间有直接的边。目标是最小化连通块的数量。解决方案采用了动态规划和状态压缩的技术,通过遍历所有可能的子集并判断是否为完全图,更新最少连通块数量。最后,代码示例展示了如何实现这一算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

nnn个点(n≤18n\leq18n18),mmm条边(m≤n∗(n−1)2m\leq\frac{n*(n-1)}{2}m2n(n1))你一个简单无向图,删去一些边(可以是0),使得图满足以下性质:

  • 任意两点aaabbb,如果aaabbb连通,那么aaabbb之间有边。

求满足条件最少的连通块数量。

思路

题目数据很小,状压走起!

首先我们设fvf_vfv表示当顶点集合为vvv时,最少的连通块数量。

然后我们先暴力枚举点集vvv,判断这个点集vvv是否为完全图。

此时我们想怎么转移。

我们可以发现当v′v'vvvv的子集时,fv=min(fv′+fv−v′)f_v=min(f_{v'}+f_{v-v'})fv=min(fv+fvv)

所以此时我们就要枚举v′v'v

我们先把v′=vv'=vv=v,然后我们接下来每次都把v′=(v′−1)&vv'=(v'-1)\&vv=(v1)&v,此时(v′−1)&v(v'-1)\&v(v1)&v肯定是vvv的子集,因为如果v′−1v'-1v1中二进制下有一位为111vvv的这一位为000,那么在(v′−1)&v(v'-1)\&v(v1)&v的时候这个111就不见了,因为1&0=01\&0=01&0=0

接下来我们来算一下时间复杂度。

在枚举v′v'v的时候,因为vvvv′v'v的在二进制下每一位的关系只有三种情况,1/01/01/01/11/11/10/00/00/0,也就是说,把所有枚举的情况乘起来,就是3n3^n3n,虽然3n≥3×1093^n\geq 3 \times 10^93n3×109,但是因为常数很小,所以还是可以过。

Code

#include<bits/stdc++.h>
using namespace std;
int n,m,mp[25][25],f[2621445];
int main() {
    int u,v;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d",&u,&v),mp[u][v]=mp[v][u]=1;
    bool flag;
    memset(f,0x3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=(1<<n)-1;i++) {
        flag=true;
        for(int j=1;j<n;j++)
            if((i>>(j-1))&1)
                for(int l=j+1;l<=n;l++)
                    if((i>>(l-1))&1&&(!mp[j][l])) {
                        flag=false;
                        break;
                    }
        if(flag)
            f[i]=1;
    }
    for(int i=1;i<=(1<<n)-1;i++)
        for(int j=i;j;j=(j-1)&i)
            f[i]=min(f[i],f[j]+f[i^j]);
    printf("%d",f[(1<<n)-1]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值