• 11.5 AM 请求


    题意

    给定一个图,请把它分为两个子图,使得每一个子图都是完全图;并且要求这两个图的边数之和最少


    解法

    先建出原图的反图,即有边连接的两个点之间不连边,没有边连接的两个点之间连一条边

    可以发现这个图可以被拆成两个完全图当且仅当每一个联通块都是一个二分图

    因为如果不是二分图(存在奇环)的话,一定有奇数个点之间互不连边,那么无论如何也无法把它们分入两个集合使得这两个集合均为完全图

    二分图染色后的新图中,同一个联通块且是相同颜色的点一定在同一个子图中,而不同联通块中即使颜色不同也可以放在一起,因为两个联通块中的点一定是互有连边的

    (f_i) 表示是否存在一个分组方式使得其中有一组的大小为 (i),枚举联通块背包转移即可


    代码

    #include <cstdio>
    #include <cctype>
    #include <bitset>
    #include <cstring>
    #include <iostream>
    
    using namespace std;
    
    const int MAX_N = 800;
    const int MAX_M = 700000;
    
    int read();
    
    typedef bitset<MAX_N> Bit;
    
    int N, M, fl, cnt;
    
    int col[MAX_N], sz[2];
    int head[MAX_N], to[MAX_M], nxt[MAX_M], cap;
    
    Bit e[MAX_N], f[MAX_N];
    
    inline void link(int u, int v) { to[++cap] = v, nxt[cap] = head[u], head[u] = cap; }
    
    void DFS(int x, int cr, int* sz) {
        col[x] = cr, sz[cr]++;
        for (int i = head[x]; i; i = nxt[i]) {
            if (col[to[i]] == -1)  DFS(to[i], cr ^ 1, sz);
            else if (col[to[i]] == cr)  fl = 1;
        }
    }
    
    int main() {
    
        N = read(), M = read();
    
        for (int i = 1; i <= M; ++i) {
            int u = read(), v = read();
            e[u][v] = e[v][u] = 1;
        }
    
        memset(col, -1, sizeof col);
        for (int i = 1; i <= N; ++i)
            for (int j = 1; j <= N; ++j)  if ((i ^ j) && !e[i][j])  link(i, j);
    
        f[0][0] = 1;
        for (int i = 1; i <= N; ++i) {
            if (col[i] != -1)  continue;
            sz[0] = sz[1] = 0;
            DFS(i, 0, sz);
            if (fl)  return puts("-1"), 0;
            ++cnt;
            for (int j = 0; j <= N; ++j) {
                if (j + sz[0] <= N && f[cnt - 1][j])  f[cnt][j + sz[0]] = 1;
                if (j + sz[1] <= N && f[cnt - 1][j])  f[cnt][j + sz[1]] = 1;
            }
        }
    
        int ans = 0x3f3f3f3f;
        for (int i = 1; i < N; ++i)  
            if (f[cnt][i])  ans = min(ans, i * (i - 1) / 2 + (N - i) * (N - i - 1) / 2);
    
        printf("%d
    ", ans);
    
        return 0;
    }
    
    int read() {
        int x = 0, c = getchar();
        while (!isdigit(c))  c = getchar();
        while (isdigit(c))   x = x * 10 + c - 48, c = getchar();
        return x;
    }
    
  • 相关阅读:
    中国剩余定理(SCAUOJ 1077)
    uva 11426 GCD
    LA 4119 Always an integer (数论+模拟)
    uva 12003 Array Transformer (线段树套平衡树)
    uva 10253 Series-Parallel Networks (整数划分+多重集)
    LA 4123 Glenbow Museum
    uva 11361 Investigating Div-Sum Property
    2013多校训练赛第二场 总结
    uva 11174 Stand in a Line (排列组合)
    canvas 画椭圆
  • 原文地址:https://www.cnblogs.com/VeniVidiVici/p/11802222.html
Copyright © 2020-2023  润新知