java實現微信紅包 拼手氣紅包
題面
題目背景
Bob 喜歡玩電腦遊戲,特別是戰略遊戲。但是他經常無法找到快速玩過遊戲的辦法。現在他有個問題。
題目描述
他要建立一個古城堡,城堡中的路形成一棵無根樹。他要在這棵樹的結點上放置最少數目的士兵,使得這些士兵能瞭望到所有的路。
注意,某個士兵在一個結點上時,與該結點相連的所有邊將都可以被瞭望到。
請你編一程式,給定一樹,幫 Bob 計算出他需要放置最少的士兵。
輸入格式
第一行一個整數\(n\),表示樹中結點的數目。
第二行至第\(n+1\) 行,每行描述每個結點資訊,依次為:一個整數\(i\),代表該結點標號,一個自然數 \(k\),代表後面有\(k\) 條無向邊與結點 \(i\)
對於一個\(n\) 個結點的樹,結點標號在 \(0\) 到 \(n-1\) 之間,在輸入資料中每條邊只出現一次。保證輸入是一棵樹。
輸出格式
輸出檔案僅包含一個整數,為所求的最少的士兵數目。
輸入輸出樣例
輸入 #1複製
4
0 1 1
1 2 2 3
2 0
3 0
輸出 #1複製
1
說明/提示
資料規模與約定
對於全部的測試點,保證 \(1 \leq n \leq 1500\)
題解
因為是樹形結構,並且因為是求最值的問題,所以我們就想到了用樹形\(dp\)
一般以節點的深淺(子樹從小到大)的順序作為\(DP\)的階段,\(DP\)的狀態表示中,第一維通常是節點的
編號,(代表以該節點為根的子樹)
所以我們設\(f[i][0/1]\)表示該節點放還是不放士兵
根據題意,如果當前的節點不放士兵,那麼它的子節點必須全部放置士兵,因為要滿足士兵可以看到所
有的邊。因此我們有狀態轉移方程\(f[u][0]+=f[v][1]\)(其中v是u的子節點)
如果當前的節點放置
那麼我們可以取最小值\(f[u][1]+=min(f[v][0],f[v][1])\)
注意:可以選擇任意一個節點為根,答案都是一樣的
最後我們的答案就是\(min(f[選擇的節點][1],f[選擇的節點][0])\)
程式碼
#include<bits/stdc++.h>
using namespace std;
const int N=50000;
int f[N][2];
int happy[N];
int n;
int din[N];
int ne[N],ver[N],head[N],idx;
void add(int x,int y)
{
ne[idx]=head[x];
ver[idx]=y;
head[x]=idx;
idx++;
}
void dp(int x,int father)
{
f[x][1]=1;
for(int i=head[x];i!=-1;i=ne[i])
{
int y=ver[i];
if(y==father)continue;
dp(y,x);
f[x][0]+=f[y][1];
f[x][1]+=min(f[y][0],f[y][1]);
}
}
int main()
{
memset(head,-1,sizeof(head));
cin>>n;
for(int i=1;i<=n;i++)
{
int a,k;
cin>>a>>k;
for(int i=1;i<=k;i++)
{
int q;
cin>>q;
add(q,a);
add(a,q);
}
}
dp(0,-1);
cout<<min(f[0][0],f[0][1]);
return 0;
}