最近遇到了写树状dp的题看着有点意思,就学习了一下,从0开始,从水题开始。首先做了一道POJ上的水题Rebuilding Roads(POJ1947)
首先,这道题的输入储存是我以前没有用过的,用father[]储存父亲,而用son[]储存最后一个儿子,而brother[]则储存了相邻的兄弟节点,这样的方法是这次get的技能之一。
其次这道题就是dp的转移dp[i][j]记录下第i个节点构成节点数为j的子树需要除去的最少的边数。但是不算去除自己父亲节点的边,状态转移方程在代码中。
代码中还是有不清楚的地方,就是在状态转移方程中有一段代码#include <iostream> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <functional> #include <cstdio> #include <queue> #include <map> #include <algorithm> #include <stack> #include <utility> typedef long long ll; using namespace std; const int mx = 155; int father[mx],son[mx],brother[mx]; int mi[mx][mx],n,p; int root; void dfs(int point) { int k; for(k = 0; k <= p; k++) mi[point][k] = 1000000000; mi[point][1] = 0; k = son[point]; while(k) { dfs(k); for(int i = p; i >= 1; i--) { mi[point][i] = mi[point][i] + 1; for(int j = 1; j < i; j++) mi[point][i] = min(mi[point][i],mi[k][i - j] + mi[point][j]); } k = brother[k]; } } int main () { int i,j,k,l,ans; while(~scanf("%d%d",&n,&p)) { memset(father,0,sizeof(father)); memset(son,0,sizeof(son)); memset(brother,0,sizeof(brother)); memset(mi,0,sizeof(mi)); for(i = 1; i < n; i++) { scanf("%d%d",&l,&k); father[k] = l; brother[k] = son[l]; son[l] = k; } for(i = 1; i <= n; i++) if(father[i] == 0) break; dfs(i); ans = mi[i][p]; for(int i = 1; i <= n; i++) ans = min (ans,mi[i][p] + 1); printf("%d\n",ans); } return 0; }不清楚是做什么用的,但是去了之后却无法AC,以后再想想有待更新mi[point][i] = mi[point][i] + 1;
本文介绍了如何通过学习水题RebuildingRoads来理解树状DP的概念,包括使用特殊的节点存储方式(父亲节点、最后一个儿子节点、相邻兄弟节点),以及实现状态转移方程来解决最小化边数的问题。代码示例展示了如何通过DFS遍历树结构,并计算构成特定子树所需的最少边数。
树状dp&spm=1001.2101.3001.5002&articleId=45295827&d=1&t=3&u=b7d42197e0284a329fbbad1270f306ab)
172

被折叠的 条评论
为什么被折叠?



