huaqinglv

Atcoder abc187 F Close Group

题目

给出一张n个点,m条边的无向图,问删除任意数量的边后,留下来的最少数量的团的个数(\(n \le 18\)

题解

核心:枚举状态+动态规划

第一次枚举状态,对状态进行预处理,判断状态里所有的1是否能够形成一个团

第二次枚举状态S,再对每个状态枚举子状态T,假如T是一个团,那么 就可以进行动态递推

\[dp[S]=min(dp[S],dp[S\wedge T]+1) \]

复杂度分析

状态S中1的个数有x个,那S的子集就有 \(2^x\) 个,同时这样的S有 \(C_{18}^x\)

所以最后的循环次数为

\[C_{18}^0*2^0+C_{18}^1*2^1+\cdots+C_{18}^x*2^x+\cdots=(1+2)^{18} \]

最坏的情况是\(3^{18}\) ,3e8的复杂度

代码

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<map>
#include<cstring>
using namespace std;
int a[100][100];
const int N=1e6;
int L[N],dp[N];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        a[x][y]=1;
        a[y][x]=1;
    }
    int R=1<<n;
    for(int S=0;S<R;S++){
        int flag=1;
        for(int i=1;i<=n;i++){
            int x1=i-1;
            if(!((S>>x1)&1)) continue;
            for(int j=i+1;j<=n;j++){
                int x2=j-1;
                if(!((S>>x2)&1)) continue;
                if(a[i][j]==0){
                    flag=0;
                    break;
                }
            }
            if(flag==0) break;
        }
        L[S]=flag;
    }
    for(int S=1;S<R;S++){
        dp[S]=n+1;
        for(int T=S;T;T=(T-1)&S){
            if(L[T]) dp[S]=min(dp[S],dp[T^S]+1);
        }
    }
    printf("%d\n",dp[R-1]);
}

相关文章: