1. 程式人生 > 實用技巧 >P2458 [SDOI2006]保安站崗 題解

P2458 [SDOI2006]保安站崗 題解

P2458 [SDOI2006]保安站崗 題解

間隙(原題面)

  • 前排宣告:蒟蒻剛學OI沒多久,講的可能比較囉嗦,望見諒

大致題意

給一顆樹,每個點都可以花費一定的價格來放置一名"保安"

每個保安都可以看管他本身所在的點和所有與他所站的點相鄰的點

求:看管所有點所需要的最小花費

分析

樹形dp。

先來說一種錯誤的做法,也是我一開始想到的做法

每個點都有"放置"和"不放置"兩種選擇

\(dp[i][0]\)為第\(i\)個點"不放置"保安所需要的最小花費

\(dp[i][1]\)為第\(i\)個點“放置”保安所需要的最小花費

如果第\(i\)個點"放置"了保安

那它的下一個節點則可以選擇"放或不放"兩種決策

反之,下一個節點必須都"放置"一名保安

很明顯是錯的

放張圖應該就明白了

(下一個節點不一定要由父親或自己來看管,也可以由自己的"兒子"來看管)

也就是說,每個點的看管物件都有:

  • 自己
  • 父親
  • 兒子

三種可能

如何轉移

\(dp[i][0]\)為該點由自己看管所產生的最有解

\(dp[i][1]\)為該點由父親看管所產生的最優解

\(dp[i][2]\)為該點由兒子看管所產生的最優解

  • 1.由“自己”看管

自己的位置上已經"放置了"一個點

那麼它的所有兒子就都會被自己所"看管"住

顯然兒子可以選擇任意一種決策

  • 得到轉移方程:\(dp[i][0]=\sum min(dp[son][0],dp[son][1],dp[son][2])+w[i]\)

(\(w[i]\)為父親節點"放置"守衛所需要的價值)

  • 2.由“父親”看管

自己由父親看管,說明自己所在的點上未"放置"門衛,那兒子肯定只能由自己的兒子看管或由自己看管

  • 得到方程:\(dp[i][1]=\sum min(dp[son][0],dp[son][2])\)

  • 3.由“兒子”看管

(圖中紅藍分別為兩種可能情況)

既然是由自己的兒子看管

兒子的決策也有兩種可能

1.由兒子的"兒子"看管

2.由自己看管

  • 得到方程:\(dp[i][2]=\sum min(dp[son][2],dp[son][0])\)

有一種極端情況,如果全部都選了\(dp[son][2]\)

"自己"就會產生無人看管的情況

因此要在這裡加一個小特判,具體程式碼裡有解釋

這裡做了個簡陋的gif,不懂的可以結合程式碼看一下

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1510;
int n,dp[MAXN][4],w[MAXN];
int is_head[MAXN];
vector <int> son[MAXN];
void dfs(int x){
	bool is_cs = false;//用來判斷有無極端情況
	 
	int minn = 0x3ffffff;//用來求極端情況的最小值 
	dp[x][0] = w[x];
	for(int i=0;i<son[x].size();i++){
		int v = son[x][i];
		dfs(v);
		dp[x][0]+=min(min(dp[v][0],dp[v][1]),dp[v][2]);//由自己看守 
		dp[x][1]+=min(dp[v][2],dp[v][0]);//由父親看守 
		//由兒子看守 ↓ 
		if(dp[v][0]<dp[v][2]){ 
			dp[x][2]+=dp[v][0];//如果兒子放置守衛花費的錢更少,那就直接在兒子的點上放置一個守衛 
			is_cs=true;//既然兒子的位置上已經放置守衛了,無極端情況存在 
		}
		else{//否則在兒子的兒子上放置守衛 
			minn = min( minn , dp[v][0]-dp[v][2]);//計算最小所需值 
			dp[x][2]+=dp[v][2];
		}
	}
	if(!is_cs) dp[x][2]+=minn;//如果存在極端情況,則加上差值,相當於是消掉dp[-][2],加上dp[-][0] 
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		int u,m;
		scanf("%d%d",&u,&m);
		w[u]=m;
		int k;
		scanf("%d",&k);
		for(int i=1;i<=k;i++){
			int v;
			scanf("%d",&v);
			is_head[v]++;
			son[u].push_back(v);
		}
	}
	for(int i=1;i<=n;i++){
		if(!is_head[i]){
			dfs(i);
			cout<<min(dp[i][0],dp[i][2]);
			return 0;
		}
	}
}