1. 程式人生 > 程式設計 >java實現微信紅包 拼手氣紅包

java實現微信紅包 拼手氣紅包

題面

題目背景

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

題目描述

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

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

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

輸入格式

第一行一個整數\(n\),表示樹中結點的數目。

第二行至第\(n+1\) 行,每行描述每個結點資訊,依次為:一個整數\(i\),代表該結點標號,一個自然數 \(k\),代表後面有\(k\) 條無向邊與結點 \(i\)

相連。接下來 \(k\) 個整數,分別是每條邊的另一個結點標號 \(r_1,r_2,\cdots,r_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;
}