CF906C Party題解

yingxilin發表於2024-07-20

今天來水一波題解……

理解題意

由於題目意思講得很清楚,就因為懶惰直接複製了……

給你一堆一對對的關係,然後每一個關係對代表兩個人認識。然後你每次可以選擇一個人i,讓i認識的所有人都相互認識,即i把介紹自己所有的朋友給其他人。然後現在問你最少需要選擇多少個這樣的i,使得所有的人都相互認識。

注意了,朋友的朋友並不一定互為朋友,否則就用並查集秒了(這看顏色紫紫的就知道不是這樣)。

思路

用狀壓DP

定義一個陣列DP[s],s表示互為朋友的人的狀態,例如101001表示第1個,第4個和第6個人的互為朋友,而DP[s]表示實現s這個狀態所需的介紹次數。

狀態轉移方程:

dp[s|fr[i]]=min(dp[s|fr[i]],dp[s]+1);

dp[s|fr[i]]表示在狀態s中,i介紹朋友後的狀態,fr[i]表示i的朋友的集合,記住i也認識它自己!!!

那麼初始化呢,因為i的朋友只需i來叫一次,so……fr[i]=1。

注意如果輸入的關係即可使所有人互為朋友,則應輸出0,要特判。

接下來貼上我醜陋的程式碼

ACcode

#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=(1<<22)+1000;
int fr[N];//fr[i]-->i的朋友
int s[N];//人的集合
int dp[N];//使集合i的人認識所需介紹次數
int q[N];//q[s]=i表示s的轉態這次由i介紹得到
int pre[N];//存s經過這次i介紹之前的狀態
const int inf=0x3f3f3f3f;
void dfs(int x)
{
	if(pre[x]) dfs(pre[x]);
	cout<<q[x]<<" ";//在回溯時輸出
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	memset(dp,0x3f,sizeof dp);
	cin>>n>>m;
	for(int i=1;i<=n;i++) fr[i]+=1<<(i-1);//自己認識自己
	for(int i=1;i<=m;i++)
	{
		int u,v;
		cin>>u>>v;
		fr[u]+=1<<(v-1);
		fr[v]+=1<<(u-1);
        //v,u互為朋友
	}
	if(m==n*(n-1)/2)//原本互為朋友,特判
	{
		cout<<0;
		return 0;
	}
	for(int i=1;i<=n;i++)
	{
		 dp[fr[i]]=1;//i介紹fr[i]需要一步
		 q[fr[i]]=i;//fr[i]由i介紹
	}
	for(int s=0;s<=(1<<n)-1;s++)//列舉狀態
	{
		if(dp[s]==inf) continue;//當前集合無法達到
		for(int i=1;i<=n;i++) 
		{
			if((s&(1<<i-1)))//滿足s包含i
			{
				if(dp[s|fr[i]]>dp[s]+1)
				{
					dp[s|fr[i]]=dp[s]+1;//更新
					q[s|fr[i]]=i;//存當前的介紹人
					pre[s|fr[i]]=s;//存i介紹前的狀態
				}
				
			}
		}
	}
	cout<<dp[(1<<n)-1]<<endl;//輸出步數
	dfs((1<<n)-1);//輸出介紹人
	return 0;
}

本蒟蒻洛谷第一遍題解&&AC第一道紫題,如有不足請指教。

求贊,QWQ

完結撒花

相關文章