消防局的設立

消防局的設立

2020 年,人類在火星上建立了一個龐大的基地羣,總共有 n 個基地。起初爲了節約材料,人類只修建了 n-1 條長爲 1 的道路來連接這些基地,並且每兩個基地都能夠通過道路到達,所以所有的基地形成了一個巨大的樹狀結構。

由於火星上非常乾燥,經常引發火災,人類決定在火星上修建若干個消防局。消防局只能修建在基地裏,每個消防局有能力撲滅與它距離不超過 2 的基地的火災。

你的任務是計算至少要修建多少個消防局才能夠確保火星上所有的基地在發生火災時,消防隊有能力及時撲滅火災。

輸入格式

輸入文件的第一行爲 n,表示火星上基地的數目。
接下來的 n-1 行每行有一個正整數,其中文件第 i 行的正整數爲 ai,表示從編號爲 i 的基地到編號爲 a1 的基地之間有一條道路,爲了更加簡潔的描述樹狀結構的基地羣,有 ai < i。

輸出格式

輸出文件僅有一個正整數,表示至少要設立多少個消防局纔有能力及時撲滅任何基地發生的火災。

解法

這是一道稍複雜的樹形DP,對於每個點,我定義了5個狀態,分別爲dp[ i ][ 1 ~ 5 ],表示爲 i 點提供消防服務的是 i 的①爺爺/兄弟、②父親、③自己、④一個兒子、⑤一個孫子。

具體這樣計算:

dfs(int x) {
	dp[x][3] = 1;
	int dpp1 = 0x7f7f7f7f;
	int dpp2 = 0x7f7f7f7f;
	枚舉兒子 {
		dfs(兒子);
		int y = 兒子;
		dp[x][1] += min(dp[y][3],min(dp[y][4],dp[y][5]));
		dp[x][2] += min(dp[y][1],min(dp[y][3],min(dp[y][4],dp[y][5])));
		dp[x][3] += min(dp[y][2],min(dp[y][3],min(dp[y][4],dp[y][5])));
		dp[x][4] += min(dp[y][1],min(dp[y][3],min(dp[y][4],dp[y][5])));
		dpp1 = min(dpp1,dp[y][3] - min(dp[y][1],min(dp[y][3],min(dp[y][4],dp[y][5]))));
		dp[x][5] += min(dp[y][3],min(dp[y][4],dp[y][5]));
		dpp2 = min(dpp2,dp[y][4] - min(dp[y][3],min(dp[y][4],dp[y][5])));
	}
	dp[x][4] += dpp1;
	dp[x][5] += dpp2;
}

可見此題的狀態轉移方程很複雜。

CODE

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#define LL long long
using namespace std;

LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s == '-')f = -1;s = getchar();}
	while(s >= '0' && s <= '9') {x = x * 10 + s - '0';s = getchar();}
	return x * f;
}//讀入優化
LL n,m,i,j,s,o,k;
vector<int> g[1005];//樹
int dp[1005][10]; 
LL min(LL a,LL b) {
	return a < b ? a : b;
}
void dfs(int x,int fa) {
	dp[x][1] = dp[x][2] = dp[x][3] = dp[x][4] = dp[x][5] = 0;
	dp[x][3] = 1;
	int dpp1 = 0x7f7f7f7f;
	int dpp2 = 0x7f7f7f7f;
	for(int i = 0;i < g[x].size();i ++) {
		if(g[x][i] != fa) {
			dfs(g[x][i],x);
			int y = g[x][i];
            //以下是核心部分
			dp[x][1] += min(dp[y][3],min(dp[y][4],dp[y][5]));
			dp[x][2] += min(dp[y][1],min(dp[y][3],min(dp[y][4],dp[y][5])));
			dp[x][3] += min(dp[y][2],min(dp[y][3],min(dp[y][4],dp[y][5])));
			dp[x][4] += min(dp[y][1],min(dp[y][3],min(dp[y][4],dp[y][5])));
			dpp1 = min(dpp1,dp[y][3] - min(dp[y][1],min(dp[y][3],min(dp[y][4],dp[y][5]))));
			dp[x][5] += min(dp[y][3],min(dp[y][4],dp[y][5]));
			dpp2 = min(dpp2,dp[y][4] - min(dp[y][3],min(dp[y][4],dp[y][5])));
		}
	}
	dp[x][4] += dpp1;
	dp[x][5] += dpp2;
	return ;
}
int main() {
	n = read();
	for(int i = 2;i <= n;i ++) {
		s = read();
		g[i].push_back(s);
		g[s].push_back(i);
	}
	dfs(1,0);
	printf("%d\n",min(dp[1][3],min(dp[1][4],dp[1][5])));
	return 0; 
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章