最近在研究树形DP,所以做了几道比较典型的树形DP的题目。大多数都是一些比较经典的题,大部分都用了多叉转二叉,题目也比较类似。感觉树形DP的模式都比较固定,基本都是先建树,然后dfs,方程也基本都是枚举左子树和右子树各自分配资源得到根的最大值,具体见题目吧。
1、rqnoj30:愚蠢的矿工
#include<cstdio> #include<cstring> using namespace std; const int maxn = 1000 + 10; const int maxm = 100 + 10; struct tree { int l,r; tree() { l = r = -1; } }Tree[maxn]; int n,m; int w[maxn]; int f[maxn][maxm]; bool map[maxn][maxn]; void init() { freopen("rqnoj30.in","r",stdin); freopen("rqnoj30.out","w",stdout); } void readdata() { memset(map,false,sizeof(map)); scanf("%d%d",&n,&m); for(int i = 1;i <= n;i++) { scanf("%d",&w[i]); } for(int i = 1;i <= n;i++) { int a,b; scanf("%d%d",&a,&b); map[a][b] = true; } } void maketree(int k) { for(int i = 1;i <= n;i++) { if(map[k][i]) { if(Tree[k].l == -1) { Tree[k].l = i; } else { int t = Tree[k].l; while(Tree[t].r != -1)t = Tree[t].r; Tree[t].r = i; } } } if(Tree[k].l != -1)maketree(Tree[k].l); if(Tree[k].r != -1)maketree(Tree[k].r); } int max(int a,int b) { return a > b ? a : b; } int dfs(int d,int k) { if(d == -1)return 0; if(f[d][k] != -1)return f[d][k]; f[d][k] = dfs(Tree[d].r,k); for(int i = 0;i <= k - 1;i++) { f[d][k] = max(dfs(Tree[d].l,i) + dfs(Tree[d].r,k-i-1) + w[d],f[d][k]); } return f[d][k]; } void solve() { memset(f,-1,sizeof(f)); for(int i = 1;i <= n;i++)f[i][0] = 0; maketree(0); printf("%d",dfs(0,m+1)); } int main() { init(); readdata(); solve(); return 0; }
2、ural1018:二叉苹果树
#include<cstdio> #include<cstring> using namespace std; const int maxn = 100 + 10; struct tnode { int l,r; }tree[maxn]; int map[maxn][maxn]; int w[maxn]; int f[maxn][maxn]; int n,q; void init() { freopen("ural1018.in","r",stdin); freopen("ural1018.out","w",stdout); } void readdata() { memset(map,-1,sizeof(map)); scanf("%d%d",&n,&q); for(int i = 1;i < n;i++) { int l,r,w; scanf("%d%d%d",&l,&r,&w); map[l][r] = map[r][l] = w; } } void maketree(int v) { for(int i = 1;i <= n;i++) { if(map[v][i] >= 0) { tree[v].l = i; w[i] = map[v][i]; map[v][i] = map[i][v] = -1; maketree(i); break; } } for(int i = 1;i <= n;i++) { if(map[v][i] >= 0) { tree[v].r = i; w[i] = map[v][i]; map[v][i] = map[i][v] = -1; maketree(i); break; } } } int max(int a,int b) { return a > b ? a : b; } void dfs(int v,int k) { if(k == 0) f[v][k] = 0; else if(tree[v].l == 0 && tree[v].r == 0) { f[v][k] = w[v]; } else { f[v][k] = 0; for(int i = 0;i < k;i++) { if(f[tree[v].l][i] == 0)dfs(tree[v].l,i); if(f[tree[v].r][k-i-1] == 0)dfs(tree[v].r,k-i-1); f[v][k] = max(f[v][k],f[tree[v].l][i]+f[tree[v].r][k-i-1] + w[v]); } } } void solve() { maketree(1); dfs(1,q+1); int ans = f[1][q+1]; printf("%d",ans); } int main() { init(); readdata(); solve(); return 0; }
3、tyvj1051:选课
#include<cstdio> #include<cstring> using namespace std; const int maxn = 300 + 10; struct tree { int l,r; tree() { l = r = -1; } }Tree[maxn]; bool map[maxn][maxn]; int num[maxn]; int f[maxn][maxn]; int n,m; void init() { freopen("tyvj1051.in","r",stdin); freopen("tyvj1051.out","w",stdout); } void readdata() { memset(map,false,sizeof(map)); scanf("%d%d",&n,&m); for(int i = 1;i <= n;i++) { int t,w; scanf("%d%d",&t,&w); map[t][i] = true; num[i] = w; } } void maketree(int k) { for(int i = 1;i <= n;i++) { if(map[k][i]) { if(Tree[k].l == -1) { Tree[k].l = i; } else { int t = Tree[k].l; while(Tree[t].r != -1)t = Tree[t].r; Tree[t].r = i; } } } if(Tree[k].l != -1)maketree(Tree[k].l); if(Tree[k].r != -1)maketree(Tree[k].r); } int max(int a,int b) { return a > b ? a : b; } int dfs(int t,int m) { if(t == -1)return 0; if(f[t][m] != -1)return f[t][m]; f[t][m] = dfs(Tree[t].r,m); for(int i = 0;i <= m - 1;i++) { f[t][m] = max(dfs(Tree[t].l,i) + dfs(Tree[t].r,m-i-1) + num[t],f[t][m]); } return f[t][m]; } void solve() { memset(f,-1,sizeof(f)); for(int i = 1;i <= n;i++)f[i][0] = 0; maketree(0); printf("%d",dfs(0,m+1)); } int main() { init(); readdata(); solve(); return 0; }
4、tyvj1052:没有上司的舞会
#include<cstdio> #include<cstring> using namespace std; const int maxn = 6000 + 10; struct tree { int l,r; tree() { l = r = -1; } }Tree[maxn]; int root,n; int f[maxn][2]; int w[maxn]; bool map[maxn][maxn]; bool flag[maxn]; void init() { freopen("tyvj1052.in","r",stdin); freopen("tyvj1052.out","w",stdout); } void readdata() { memset(flag,true,sizeof(flag)); memset(map,false,sizeof(map)); scanf("%d",&n); for(int i = 1;i <= n;i++) { scanf("%d",&w[i]); } for(int i = 1;i < n;i++) { int l,k; scanf("%d%d",&l,&k); map[k][l] = true; flag[l] = false; } for(int i = 1;i <= n;i++) { if(flag[i]) { root = i; break; } } } void maketree(int k) { for(int i = 1;i <= n;i++) { if(map[k][i]) { if(Tree[k].l == -1) { Tree[k].l = i; } else { int t = Tree[k].l; while(Tree[t].r != -1)t = Tree[t].r; Tree[t].r = i; } } } if(Tree[k].l != -1)maketree(Tree[k].l); if(Tree[k].r != -1)maketree(Tree[k].r); } int max(int a,int b) { return a > b ? a : b; } int dfs(int k,int s) { if(f[k][s] != -1)return f[k][s]; f[k][s] = 0; if(s == 1)f[k][s] = w[k]; for(int i = Tree[k].l;i != -1;i = Tree[i].r) { int com; if(s == 0) { com = max(dfs(i,0),dfs(i,1)); if(com >= 0)f[k][s] += com; } if(s == 1) { com = dfs(i,0); if(com >= 0)f[k][s] += com; } } return f[k][s]; } void solve() { memset(f,-1,sizeof(f)); maketree(root); int ans = max(dfs(root,0),dfs(root,1)); printf("%d",ans); } int main() { init(); readdata(); solve(); return 0; }
5、tyvj1513:小胖守皇宫
这道题题意有点不好理解,每个节点有三种状态:第一种是在该节点放士兵;第二种是在该节点不放,但在它的儿子节点放了士兵;第三种是该节点不放,它被父亲节点影响。所以第一种情况可以由三种情况转移,第二种情况能由1,2种情况转移,第三种情况只能由第二种情况来转移。
#include<cstdio> #include<cstring> using namespace std; const int inf = 0x3ffff; const int maxn = 1500 + 10; const int maxm = 5; struct tree { int l,r; tree() { l = r = -1; } }Tree[maxn]; int w[maxn]; int f[maxn][maxm]; int n,root; bool vis[maxn][maxm]; bool map[maxn][maxn]; bool flag[maxn]; void init() { freopen("tyvj1513.in","r",stdin); freopen("tyvj1513.out","w",stdout); } void readdata() { memset(flag,false,sizeof(flag)); memset(map,false,sizeof(map)); scanf("%d",&n); for(int i = 1;i <= n;i++) { int t1,m; scanf("%d",&t1); scanf("%d",&w[t1]); scanf("%d",&m); for(int j = 1;j <= m;j++) { int t2; scanf("%d",&t2); map[t1][t2] = true; flag[t2] = true; } } for(int i = 1;i <= n;i++) { if(flag[i] == false) { root = i; break; } } } void maketree(int k) { for(int i = 1;i <= n;i++) { if(map[k][i]) { if(Tree[k].l == -1) { Tree[k].l = i; } else { int t = Tree[k].l; while(Tree[t].r != -1)t = Tree[t].r; Tree[t].r = i; } } } if(Tree[k].l != -1)maketree(Tree[k].l); if(Tree[k].r != -1)maketree(Tree[k].r); } int min(int a,int b) { return a < b ? a : b; } int dfs(int k,int s) { if(vis[k][s])return f[k][s]; vis[k][s] = true; int sum = 0,temp = inf; for(int i = Tree[k].l;i != -1;i = Tree[i].r) { if(s == 1) { f[k][s] += min(dfs(i,1),min(dfs(i,2),dfs(i,3))); } if(s == 2) { f[k][s] += min(dfs(i,1),dfs(i,2)); if(f[i][1] - min(f[i][1],f[i][2]) < temp) { temp = f[i][1] - min(f[i][1],f[i][2]); } } if(s == 3) { f[k][s] += dfs(i,2); } } if(s == 1)f[k][s] += w[k]; if(s == 2)f[k][s] += temp; return f[k][s]; } void solve() { memset(vis,false,sizeof(vis)); memset(f,0,sizeof(f)); maketree(root); int ans = min(dfs(root,1),dfs(root,2)); printf("%d",ans); } int main() { init(); readdata(); solve(); return 0; }
The End...