zoukankan      html  css  js  c++  java
  • ABC 187 F. Close Group

    题意

    给一个无向图, (N) 个顶点 (M) 条边, 问删除若干条边之后最少组成多少个连通分量,使得每个联通分量都是完全图。

    (1 le N le 18) , (0 le M le dfrac {N(N-1)} 2)

    思路

    比赛的时候一直在想搜索,每次找出一个最大的完全子图,然后删掉,看删多少次。

    其实这样的想法是错误的,比如给出一个这样的图

    实际上用两个联通分量 {4,5,6} 和 {1,2,3} 就可以了,如果找最大的就会找到 {2,3,4,5} {1} {6}

    这样三个联通分量

    解法

    (O(n^22^n)) 复杂度预处理出所有完全子图, 然后枚举所有状态的子集转移即可

    对于一个集合 (S) ,枚举它的所有子集 :

    for (int i = (S - 1) & S;i ;i = (i - 1) & S)

    对于所有情况的所有子集,考虑有 (k) 个二进制位的数 (a) 这样的数有 (C_n^k) 个,子集有 (2^k)

    所以所有的子集

    [S = sum_{i = 0}^nC_n^i2^i = 3^n ]

    所以这一步的复杂度是 (O(3^n))

    总的复杂度是 (O(n^22^n + 3^n))

    ps:

    枚举 n 个 bit 中有 k 个 1 的枚举

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = 2e5 + 10;
    typedef long long ll;
    
    void print(int x,int n){
        for(int i = n - 1;i >= 0;i--){
            printf("%d", (x >> i) & 1);
        }puts("");
    }
    void EnumK(int k, int n)/*求出总共n个状态中,有k个状态为1的所有情况*/
    {
        int x, y;
        for (int i = (1 << k) - 1;i < (1 << n);i = ((i & ~y) / x >> 1) | y) {
            print(i, n);
            x = i & -i, y = i + x;
        }
    }
    
    int main(){
        int k, n;
        while(cin >> k >> n){
            EnumK(k, n);
        }
    }
    
    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = 2e5 + 10;
    typedef long long ll;
    int g[20][20], f[1 << 18], n, m, x, y;
    int main(){
        scanf("%d%d", &n, &m);
        while (m--) { 
            scanf("%d%d", &x, &y); 
            x--, y--; 
            g[x][y] = g[y][x] = 1;
        }
        memset(f, 0x3f, sizeof f);
        
        for(int i = 0;i < (1 << n);i++){
            bool ok = true;
            for(int j = 0;j < n;j++){
                if (!((i >> j) & 1))continue;
                for(int k = j + 1;k < n;k++){
                    if (!((i >> k) & 1)) continue;
                    if(!g[j][k]){
                        ok = false;
                        break;
                    }
                }if (not ok)break;
            }
            if (ok)f[i] = 1;
        }
        
        for(int i = 0;i < (1 << n);i++){
            for(int j = (i - 1) & i;j ;j = (j - 1) & i){
                f[i] = min(f[i], f[j] + f[i ^ j]);
            }
        }
        printf("%d
    ", f[(1 << n) - 1]);
    }
    
  • 相关阅读:
    什么是反向代理,反向代理有什么好处?
    动态的favicons
    管理emacs dotemacs文件
    自己的部分小软件合计 2000 2013(很久没有更新了)
    经典CSS 横向导航菜单【下载】
    Web 调试,兼容浏览器的在线调试(系列)
    sql server 执行大脚本时报警告没有足够的内存继续执行程序
    c#对接顺丰图片文件
    Sql Server行转列 动态拼接sql
    centos6.2更新yum源
  • 原文地址:https://www.cnblogs.com/sduwh/p/14313754.html
Copyright © 2011-2022 走看看