題目描述
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;
}