[洛谷]P2016 戰略遊戲 (#樹形dp)

題目描述

Bob喜歡玩電腦遊戲,特別是戰略遊戲。但是他經常無法找到快速玩過遊戲的辦法。現在他有個問題。

他要建立一個古城堡,城堡中的路形成一棵樹。他要在這棵樹的結點上放置最少數目的士兵,使得這些士兵能瞭望到所有的路。

注意,某個士兵在一個結點上時,與該結點相連的所有邊將都可以被瞭望到。

請你編一程序,給定一樹,幫Bob計算出他需要放置最少的士兵.

輸入格式

第一行 N,表示樹中結點的數目。

第二行至第N+1行,每行描述每個結點信息,依次爲:該結點標號i,k(後面有k條邊與結點I相連)。

接下來k個數,分別是每條邊的另一個結點標號r1,r2,...,rk。

對於一個n(0<n<=1500)個結點的樹,結點標號在0到n-1之間,在輸入數據中每條邊只出現一次。

輸出格式

輸出文件僅包含一個數,爲所求的最少的士兵數目。

例如,對於如下圖所示的樹:

       0
1
2      3

答案爲1(只要一個士兵在結點1上)。

輸入輸出樣例

輸入 #1複製

4
0 1 1
1 2 2 3
2 0
3 0

輸出 #1複製

1

思路

突然發現樹形dp的本質----每個節點都可以成爲根節點,然後在這個節點搜索它的子樹獲取最優解。

令dp[i][0/1]爲在節點i上放或不放士兵,0爲不放,1爲放。如果節點i上不放士兵,就意味着節點i的子節點必須要放士兵。則:

1.如果節點i不放士兵

dp[i][0]=dp[i][0]+dp[son][1]

其中son是i的子節點。

2.如果節點i放士兵

dp[i][1]=dp[i][1]+min(dp[son][0],dp[son][1])

答案爲min(dp[root][0],dp[root][1])。

#include <stdio.h>
#include <iostream>
#include <memory.h>
#define maxn 1501
using namespace std;
int n,m,s,cnt,head[maxn],dp[maxn][2];
struct node
{
	int nxt,to;
}e[maxn<<1];
inline void add(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
void dfs(int i,int fa)
{
	dp[i][1]=1,dp[i][0]=0;
	register int j;
	for(j=head[i];j;j=e[j].nxt)
	{
		int v(e[j].to);//子節點 
		if(v==fa) continue;//dfs多設一個父節點,判斷節點i的子節點是否和父節點相等,相等的情況要排除 
		dfs(v,i);
		dp[i][0]+=dp[v][1];
		dp[i][1]+=min(dp[v][1],dp[v][0]);
	}
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	register int i,j;
	cin>>n;
	for(i=1;i<=n;i++)
	{
		int u,v;
		cin>>u>>m;
		for(j=1;j<=m;j++)
		{
			cin>>v;
			add(u,v);
			add(v,u);
		}
	}
	dfs(0,-1);
	cout<<min(dp[0][1],dp[0][0])<<endl;
	return 0;
}

 

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