第1行:2個數N, K中間用空格分隔(1<= N <= 100000, 0 <= K <= N)。 之後N-1行:每行2個數S, E中間用空格分隔,表示編號爲S的村莊同編號爲E的村莊之間有道路相連。(0 <= S, E < N)。
輸出一個數說明要實現對全部村莊的武力控制,夾克老爺需要派出最少多少名家丁?
4 1 0 1 0 2 0 3
1
官方題解:樹形DP,貪心思想,從葉子節點向上,能不放就不放,到了k長就放一個。
後序遍歷,記錄不同子樹上傳的狀態,子樹狀態記錄爲該子樹可以向上管理的(缺少的用負數)
可能A子樹放置的家丁可以把B子樹的村莊全部覆蓋,這樣就可以節約家丁數了。
min_length = min(dp[child])
max_length = max(dp[child])
if(min_length <= -K) {
++result;
dp[cur_idx] = K; //子樹需要暴力支援深度達到K了,必須在當前位置放置一個家丁,並向上提供K的暴力輸出
} else if(max_length + min_length > 0) {
dp[cur_idx] = max_child - 1; //有一個子樹放置的家丁能夠把全部其他需要鎮壓的村莊都覆蓋
} else {
dp[cur_idx] = min_child - 1; //繼續向上級要求暴力支持
}
最後如果root的狀態小於0要額外多放一個。
這個題目真的挺好玩的,把這n個村莊n-1個連接想象成是一個樹,然後記錄每一個節點缺少的級別,當到達了k個級別的時候就要加上一個家丁,此時這個節點缺少的級別是正的k,因爲它要開始照顧到它的兄弟子樹了。
手動擴棧擴棧也是頭一次。。。
代碼:
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
#include <cstring>
#pragma warning(disable:4996)
using namespace std;
#define inf 0x3f3f3f3f
int res;
int n, k;
int s, e;
int used[100050];
int dp[100050];
vector<int>node[100050];
void dfs(int x)
{
used[x] = 1;
int minn = inf;
int maxn = -inf;
int size = node[x].size();
int i;
for (i = 0; i < size; i++)
{
int temp = node[x][i];
if (used[temp] == 0)
{
dfs(temp);
minn = min(minn, dp[temp]);
maxn = max(maxn, dp[temp]);
}
}
if (minn == inf)
{
dp[x] = -1;
}
else if(minn<=-k)
{
res++;
dp[x] = k;
}
else if (maxn + minn > 0)
{
dp[x] = maxn - 1;
}
else
{
dp[x] = minn - 1;
}
used[x] = 0;
}
int main()
{
//freopen("i.txt", "r", stdin);
//freopen("o.txt", "w", stdout);
int i;
scanf("%d%d", &n, &k);
for (i = 1; i <= n - 1; i++)
{
scanf("%d%d", &s, &e);
s++;
e++;
node[s].push_back(e);
node[e].push_back(s);
}
if (k == 0)
{
printf("%d", n);
return 0;
}
dfs(1);
if (dp[1] < 0)
{
res++;
}
printf("%d", res);
//system("pause");
return 0;
}