Rebuilding Roads 【POJ
阿新 • • 發佈:2018-12-13
題目連結
很好的一道題,WA了幾次之後算是對樹形DP有了些自己的理解,題中給你N個節點,建樹構成(N-1)條邊,其中,問你要是隻保留M個節點,需要至少刪除幾條的邊?
根據推斷,這是一道不容置疑的樹形DP,也可以說是樹形揹包,具體是什麼原因,讓我們在解題的過程中來理解其因。
我們搜到最底的葉子節點,它的節點數就是它本身,所以我們對於其定義為其實節點,我們從它開始往上遞推,到達第二個節點,有保留為兩個節點或者刪除其子節點就留一個節點,所以初始化的時候放上一個假如刪到以它為根節點且只剩它本身的話,需要刪除的是它的所有子節點,然後,假如向上,一直達到第三個節點的時候呢,那麼他可能就會有很多子節點
類似的,我們能繼續往下推,推出完整的狀態轉移方程,或者說是揹包方程,當然,我們填充完了其中一個子節點,那麼我們填充上它的下一個子節點
狀態轉移方程:
- 初始化:dp[u][1]=vt[u].size();
- dp[u][j]=min(dp[u][j], dp[u][j-k]+dp[v][k]-1);
- 最後,我們只需要遍歷所有的後續值為M的DP節點即可,當然,除了最最最前面的根節點“1”以外,其他的節點在做比較之時需要加上去除它的父節點相連的邊的那一步“+1”。
完整程式碼:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int maxN=155;
int N, M;
vector<int> vt[maxN];
int dp[maxN][maxN]; //以i為根是,要保留j個節點需要付出的代價
int dfs(int u)
{
int son=1;
int len=(int)vt[u].size();
dp[u][1]=len;
for(int i=0; i<len; i++)
{
int v=vt[u][i];
son+=dfs(v);
for(int j=son; j>=1; j--) //揹包,倒敘
{
for(int k=1; k<j; k++)
{
dp[u][j]=min(dp[u][j], dp[u][j-k]+dp[v][k]-1);
}
}
}
return son;
}
int main()
{
while(scanf("%d%d", &N, &M)!=EOF)
{
for(int i=1; i<=N; i++) vt[i].clear();
memset(dp, INF, sizeof(dp));
for(int i=1; i<N; i++)
{
int e1, e2;
scanf("%d%d", &e1, &e2);
vt[e1].push_back(e2);
}
dfs(1);
int ans=dp[1][M];
for(int i=2; i<=N; i++)
{
ans=min(ans, dp[i][M]+1);
}
printf("%d\n", ans);
}
return 0;
}
初次將樹形DP寫自己的思考,也算是因為理解到了些東西做下的筆記,若有什麼錯誤的想法,歡迎大佬指教。