1. 程式人生 > 實用技巧 >Atcoder abc187 F Close Group(動態規劃)

Atcoder abc187 F Close Group(動態規劃)

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]);
}