我们通过状态压缩,然后判断转移的情况,然后主要通过| 的运算,将不是联通块中的人的朋友加入到联通块中的操作

#include <bits/stdc++.h>
#define cl(a) memset(a,0,sizeof(a))
#define ll long long
#define pb(i) push_back(i)
#define mp make_pair
using namespace std;
const int maxn=2e5+50;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
typedef pair<int,int> PII;
ll fpow(ll n, ll k, ll p = mod) {ll r = 1; for (; k; k >>= 1) {if (k & 1) r = r * n%p; n = n * n%p;} return r;}
ll inv(ll a, ll p = mod) {return fpow(a, p - 2, p);}
//head
int conn[22];
int dp[1<<(22+1)][3];
int main()
{
    int n,m,i,j;
    scanf("%d %d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        u--;v--;
        conn[u] |= 1<<v;
        conn[v] |= 1<<u; //记录每个点相连的边的情况
    }
    for(int i=0;i<n;i++) conn[i]|=(1<<i);
    memset(dp,0x3f,sizeof(dp));
    for(i=1;i<(1<<n);i++)
    {
        for(j=0;j<n;j++)
        {
            int t=i & (1<<j);
            if(!t){
                continue;
            } //在第i种联通块中,不包含编号为j的人的情况
            if((conn[j]&i)!=i) break;// 也就是说找到了一个人j ,在i中,并且i中有的人不是j的朋友
        }
        if(j>=n) dp[i][0]=0; //所有在i中的人自己形成了一个完全图。
        for(j=0;j<n;j++)
        {
            if(!(i&(1<<j)))continue; // i中不包含编号为j的人,就跳过
            int x = i | conn[j]; //否则的话,就把j的朋友加到联通块中
            if(dp[x][0]>dp[i][0]+1) //从i到j的一个转移,dp[][1] 保存原始状态,dp[][2]保存选的人
            {
                dp[x][0]=dp[i][0]+1;
                dp[x][1]=i;
                dp[x][2]=j;
            }
        }
    }
    int x = (1<<n)-1;
    printf("%d\n",dp[x][0]);
    vector<int>ve;
    while(dp[x][0])
    {
        ve.push_back(dp[x][2]);
        x=dp[x][1];
    }
    int ed = ve.size()-1;
    for(int i=ed;i>=0;i--)
    {
        printf("%d ",ve[i]+1);
    }
    return 0;
}