2020.07.04日常總結

UVA1220 Hali-Bula的晚會 Party at Hali-Bula\color{green}{\texttt{UVA1220 Hali-Bula的晚會 Party at Hali-Bula}}

[Problem]\color{blue}{\texttt{[Problem]}}

  • 給你一棵有 n(1n200)n(1\leq n \leq 200) 個點的樹及其樹根,每個節點用一個英文字符串表示。
  • 你需要從中選擇儘可能多的點,使得沒有一個點和它的父親同時被選擇。求最多可以選出多少個點。
  • 在選出點儘可能多的前提下,輸出方案是否唯一。唯一,輸出 Yes,不唯一,輸出 No

[Soluntion]\color{blue}{\texttt{[Soluntion]}}

一道樹形 dp\texttt{dp} 題。

不考慮節點的表示,單單考慮如何求出解先。

DP 一大特色:問什麼就求什麼。它要你輸出些什麼,你就把每一項都抽象成一個狀態即可。

fu,0f_{u,0} 表示考慮以 uu 爲根的子樹且 nn 不選時最多可以選多少個點,fu,1f_{u,1} 類似,表示是 uu 選時的方案。類似的,記 du,0/1d_{u,0/1} 表示是否唯一,下標的含義與 ff 完全一致,值爲真爲唯一,假爲不唯一。

  • uu 選,則它的兒子們都必須不選,所以:

    fu,1=vson(u)fv,0f_{u,1}=\sum\limits_{v \in \texttt{son}(u)} f_{v,0}

    故,只有當所有的 dv,0d_{v,0} 爲真時,du,1d_{u,1} 才爲真。

  • uu 不選,其兒子可選可不選,所以:

    fu,0=vson(u)max{fv,0,fv,1}f_{u,0}=\sum\limits_{v \in \texttt{son}(u)} \max \{ f_{v,0},f_{v,1} \}

    考慮是否唯一:

    • fv,0=fv,1f_{v,0}=f_{v,1} 時,不唯一。

    • 當較大值對應的 dd 值爲假時,不唯一。

考慮節點的表示,很簡單,用個 map 給每個節點一個整數編號即可。

[code]\color{blue}{\texttt{[code]}}

vector<int> son[210];//兒子 
map<string,int> Name;//編號 
int f[210][2],n,t;//dp結果 
bool unq[210][2];//是否唯一 
inline void dp(int u,int fa){
	f[u][0]=0;f[u][1]=unq[u][0]=1;
	unq[u][1]=true;//先初始化數據 
	int son_number=son[u].size();
	for(int i=0;i<son_number;i++){
		register int to=son[u][i];
		if (to==fa) continue;//重要 
		dp(to,u);//先遞歸計算子兒子 
		if (!unq[to][0]) unq[u][1]=0;
		f[u][1]+=f[to][0];//u參加舞會 
		if (f[to][0]>f[to][1]){
			f[u][0]+=f[to][0];
			if (!unq[to][0])
				unq[u][0]=false;
		}
		else if (f[to][0]<f[to][1]){
			f[u][0]+=f[to][1];
			if (!unq[to][1])
				unq[u][0]=false;
		}
		else{
			f[u][0]+=f[to][0];
			unq[u][0]=false;
		}
	}
}
int main(){
	while (~scanf("%d",&n)&&n){
		Name.clear();//清空編號 
		for(int i=1;i<=n;i++)
			son[i].clear();//init
		register string name;
		cin>>name;Name[name]=t=1;
		for(int i=2;i<=n;i++){
			register string n1,n2;
			cin>>n1>>n2;//n1與n2有連邊 
			if (!Name[n1]) Name[n1]=++t;
			if (!Name[n2]) Name[n2]=++t;
			son[Name[n2]].push_back(Name[n1]);
			son[Name[n1]].push_back(Name[n2]);
		}
		dp(1,0);//遞歸進行計算 
		if (f[1][1]>f[1][0]){
			printf("%d ",f[1][1]);
			if (unq[1][1]) puts("Yes");
			else printf("No\n");
		}//千億要注意最後的輸出
		else if (f[1][0]>f[1][1]){
			printf("%d ",f[1][0]);
			if (unq[1][0]) puts("Yes");
			else printf("No\n");
		}
		else printf("%d No\n",f[1][1]);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章