A 樹形dp
題目大意:
一棵5e3的樹,可以選擇一些點,放上基站,如果u上的基站價值爲d,那麼距離u小於等於d的點都會被覆蓋,問使得整棵樹被覆蓋需要的最小价值。
題目分析
設 表示從 u這個點 ,向外最多能覆蓋 到距離爲i的點,且他的子樹都被覆蓋的最小价值。
那麼我們考慮 可能是在u這個點建立了一個價值爲i的基站,也可能是他的子樹能夠覆蓋到 i+1
因此 我們還需要統計 u這個點 距離他的i的子樹全被覆蓋所需的最小价值
定義爲 g[u][i]
所以我們考慮
表示這個v這個點最多能往外延伸到i+1的距離,(也就是刪去的v的子樹)
這樣在樹上進行dp就可以了。
考慮邊界情況
我們在計算 f[u][0] 的時候, 不能直接選擇 j ,因爲只有 u這一個點的時候也需要消耗 1的價值,所以我們特判 f[u][0]的情況
代碼詳情
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3+50;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
vector<int>G[maxn];
int g[maxn][maxn];
int f[maxn][maxn];
int n;
void dfs(int u,int fa)
{
// cout<<u<<" fa= "<<fa<<endl;
for(int i=0;i<G[u].size();i++)
{
int v = G[u][i];
if(v!=fa)
{
dfs(v,u);
for(int j=1;j<=n;j++)
{
g[u][j] += g[v][j-1];
}
}
}
f[u][n]= n; //表示 U點 覆蓋所有點需要的價值,最大是n
g[u][0] =n; // 初始化g[u][0] u的子樹全覆蓋的情況
for(int i=n-1;i>=0;i--)
{
if(i!=0)
f[u][i] = min(i+g[u][i+1],f[u][i+1]); //在某個位置新建一個基站
else if(i==0)
//特判=0的時候, f[u][0]表示子樹都被覆蓋,只覆蓋u這個點的時候也需要1個
{
f[u][i] = min(1+g[u][2],f[u][i+1]);
}
for(int j=0;j<G[u].size();j++)
{
int v = G[u][j];
if(v!=fa)
{
f[u][i] = min(f[u][i],f[v][i+1]-g[v][i]+g[u][i+1]);
}
}
}
g[u][0] =f[u][0];
for(int i=1;i<=n;i++) g[u][i] = min(g[u][i],g[u][i-1]);
//對於所有的子樹,很顯然滿足這個關係。確保更新的值合理。
}
int main()
{
scanf("%d",&n);
memset(g,0,sizeof(g));
memset(f,0,sizeof(f));
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
// cout<<1<<endl;
dfs(1,0);
// cout<<2<<endl;
printf("%d\n",g[1][0]);
return 0;
}