pku 1947 Rebuilding Roads(树形DP)

第一次写树形DP,写个总结。

为什么会有树形DP?每个节点的状态的值只跟他的子节点有关,兄弟节点之间是互不影响的。

应该怎么来计算树形DP?深搜,先把子节点处理好,再来对当前节点进行DP。

 

对于pku1947这道题目,DP[i][j]表示以i为根节点、节点数为j的子树需要去除的边的最小数量,状态转移方程有DP[root][p]=DP[root.son][k]+DP[root][p-k]-2)。

怎么来理解这个方程呢?我们一开始假设所有root和root.son之间的边是被去除了的,然后按照深搜的顺序,尝试着把他们连接上,即root.son作为root构成的子树中的一部分。需要注意的是,因为在一开始root和root.son之间的边是当作被去除了的,现在不需要要去除,所以在结果里面-2。

 

#include <string> #include <vector> #include <cmath> #include <queue> #include <algorithm> #include <iostream> #define PI 3.14159265358979323846264338327950288 #define _clr(a,b) memset(a,b,sizeof(a)) template<class T> T _abs(T a) { if(a<0) return -a;return a;} template<class T> void get_min(T& a,T b) { if(a>b) a=b;} template<class T> void get_max(T& a,T b) { if(a<b) a=b;} using namespace std; vector<int> edges[155]; int N,P; int DP[155][155]; const int INF=1000; bool flag[155]; void DFS(int root) { for(int i=0;i<edges[root].size();i++) DFS(edges[root][i]); for(int i=0;i<edges[root].size();i++) { for(int p=P;p>1;p--)//因为DP[root][p]受DP[root][p-k]影响,所以p应该是反着循环。 { for(int k=1;k<p;k++) { get_min(DP[root][p],DP[edges[root][i]][k]+DP[root][p-k]-2); } } } } int main() { _clr(flag,0); int start,end; scanf("%d%d",&N,&P); for(int i=0;i<N-1;i++) { scanf("%d%d",&start,&end); edges[start].push_back(end); flag[end]=true; } int root=1; while(flag[root]) root++; for(int i=1;i<=N;i++) { for(int j=2;j<=P;j++) { DP[i][j]=INF; } DP[i][1]=edges[i].size()+1; } DFS(root); DP[root][P]--;//整棵树的根节点是无父节点的,所以之前多加了一,现在帮它减去 int ans=INF; for(int i=1;i<=N;i++) get_min(ans,DP[i][P]); printf("%d/n",ans); return 0; }

你可能感兴趣的:(pku 1947 Rebuilding Roads(树形DP))