树形dp小结——2
来源:互联网 发布:淘宝店铺改名字 编辑:程序博客网 时间:2024/06/06 00:13
树形dp的题一般都结合着背包来用。以下的几道题都是结合着背包的思想来的
1:树形dp+分组背包 状态比较难想
之前说过在最长距离的那道题里说过,不会返回。但是有的提示需要考虑返回的节点的。
下面就是一个例子:
Apple Tree
题目大意:
给你一个苹果树,有N个节点,每个节点上都有一个一个苹果也就有一个权值,当你经过这个点将得到权值,重复走节点只能算一次,给N-1条边。问你走k步能得到的最大权值和。
dp[i][j][0]表示从i节点出发最后回到i节点花费最多j步能获得的最大值,
dp[i][j][1]表示从j节点出发最后不回到i节点花费最多j步能获得的最大值
dp[root][j][0] = max(dp[root][j-k][0] + dp[son][k-2][0])
//root->son 和 son->root 共花费2步,j-k是在其他儿子花费的步数
dp[root][j][1] = max(dp[root][j-k][0] + dp[son][k-1][1])
dp[root][j][1] = max(dp[root][j-k][1] + dp[son][k-2][0])
//只需要回到root一次,一种情况是先走其他儿子回到root再走son,
//另一种情况是先走son这个儿子并回到root再去走其他儿子
代码:#include <iostream> #include <stdio.h> #include <string.h> using namespace std; const int M=210; int wi[M]; int dp[M][M][2];//0不回来,1回来 int n,m; int ne; struct Edge { int to,nxt; }edge[M*2]; int st[M]; void add_edge(int fr,int to) { edge[ne].to=to; edge[ne].nxt=st[fr]; st[fr]=ne++; } int isu[M]; void dfs(int rt){ isu[rt]=1; for(int i=0;i<=m;i++) dp[rt][i][0]=dp[rt][i][1]=wi[rt]; for(int i=st[rt];i!=-1;i=edge[i].nxt) { int t=edge[i].to; if(isu[t]) continue; dfs(t); for(int j=m;j>=1;j--) { for(int k=1;k<=j;k++) { dp[rt][j][0]=max(dp[rt][j][0],dp[rt][j-k][1]+dp[t][k-1][0]); dp[rt][j][0]=max(dp[rt][j][0],dp[rt][j-k][0]+dp[t][k-2][1]); dp[rt][j][1]=max(dp[rt][j][1],dp[rt][j-k][1]+dp[t][k-2][1]); } } } } int main() { int a,b; while(scanf("%d%d",&n,&m)!=EOF) { memset(dp,0,sizeof(dp)); ne=0; memset(st,-1,sizeof(st)); memset(isu,0,sizeof(isu)); //m++; for(int i=1;i<=n;i++) scanf("%d",&wi[i]); for(int i=1;i<=n-1;i++) { scanf("%d%d",&a,&b); add_edge(a,b); add_edge(b,a); } dfs(1); printf("%d\n",max(dp[1][m][0],dp[1][m][1])); } return 0; }2:
The more, The Better
HDU - 1561题目大意:
ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝物。但由于地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先攻克其他某一个特定的城堡。你能帮ACboy算出要获得尽量多的宝物应该攻克哪M个城堡吗?
思路分析:给你一棵树,欲取子节点,必取父节点。问取的价值最多为多少。
简单的树形+背包
代码:
#include<iostream>#include<string.h>#include<stdio.h>#include<vector>#define MAX 500using namespace std;vector<int>q[MAX];int dp[MAX][MAX];int v[MAX];void dfs(int n,int m){ int len=q[n].size(); int k,h; dp[n][1]=v[n];//打掉该点获得的财富值 for(int i=0;i<len;i++)//去攻击他的下边的节点 { if(m>1)//次数还没有超,还能再攻击 dfs(q[n][i],m-1); for(int j=m;j>=1;j--) { h=j+1; for(int k=1;k<h;k++)//攻击子节点的次数 { if(dp[n][h]<dp[n][h-k]+dp[q[n][i]][k])//k下打非这个子树里的 dp[n][h]=dp[n][h-k]+dp[q[n][i]][k]; } } }}int main(){ int n,m; while(scanf("%d%d",&n,&m)!=EOF,n||m) { int i,p; memset(dp,0,sizeof(dp)); memset(v,0,sizeof(v)); for(int i=0;i<=n;i++) q[i].clear(); for(int i=1;i<=n;i++) { scanf("%d%d",&p,&v[i]); q[p].push_back(i); } dfs(0,m+1); cout<<dp[0][m+1]<<endl; }}3.背包问题
Rebuilding Roads
POJ - 1947题目大意:有n个点组成一个树,问至少要删除多少条边才能获得一棵有p个结点的子树
题目分析:
dp[i][j]表示以i号节点为根的子树,当有j个结点时最少需要去掉几条边。
初始化:当只有1个节点时,一定是连接它到孩子结点的所有边都去掉。
设某一孩子结点标号为v 则dp[i][j]=min(dp[i][j],dp[i][j-t]+dp[v][t]-1);
记录最小值是时,如果最小值在子树上需要加1,因为还有连接父亲结点的一条边没算。
代码:
#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<cmath>using namespace std;struct node{ int to; int next;};int dp[155][155];int brother[155];int son[155];int n,p;void dfs(int r){ for(int i=0;i<=p;i++) { dp[r][i]=1000000000; } dp[r][1]=0; for(int i=son[r];i!=-1;i=brother[i]) { int v=i; dfs(v); for(int j=p;j>=0;j--) { int tmp=dp[r][j]+1; for(int k=0;k<=j;k++) { tmp=min(tmp,dp[v][j-k]+dp[r][k]); } dp[r][j]=tmp; } }}int main(){ int u,v; while(~scanf("%d%d",&n,&p)) { int cnt=0; memset(son,-1,sizeof(son)); for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); brother[v]=son[u];//u为父节点,v的兄弟就是u的孩子 son[u]=v; } int ans; dfs(1); ans=dp[1][p]; for(int j=1;j<=n;j++) { if(ans>dp[j][p]) ans=dp[j][p]+1;//不要忘记减去父亲的那条(除了根结点) } cout<<ans<<endl; } return 0;}4.背包 +树的重心变形
题目:Tree Cutting poj2378
给一个树状图,有n个点。求出,去掉哪个点,使得剩下的每个连通子图中点的数量不超过n/2。如果有很多这样的点,就按升序输出。n<=10000
思路分析:
此题是求解树的重心的一个变形。
根据求解树的重心的方法,设f[i] 为 以i为根的子树的结点个数,那么 f[i] += {f[j] + 1 | j 为i的子结点} 。
设dp[i] 为删除结点i, 最大的连通图有多少个结点。最后答案将d[i] <= n / 2 的结点 i 输出即可。
代码:
#include<iostream>#include<string.h>#include<stdio.h>using namespace std;const int MAX=50010;struct lalala //建结构体{ int next; int to;}edge[MAX*2];int head[MAX];//前驱节点int vis[MAX];//是否访问过,做标记int dp[MAX];//记录拥有最大的节点数int num[MAX];//统计以每个结点为根的树的结点数,记为num[].int n, tot;void add(int u, int v)//建树{ edge[++tot].next=head[u]; edge[tot].to=v; head[u]=tot;}void init(){ tot=0; memset(dp,0,sizeof(dp)); memset(vis,0,sizeof(vis)); memset(num,0,sizeof(num)); memset(edge,0,sizeof(edge));}void dfs(int u){ vis[u]=1; num[u]=1; for(int i=head[u];i!=0;i=edge[i].next) { int v=edge[i].to; if(vis[v]) continue; dfs(v); dp[u]=max(dp[u],num[v]); num[u]+=num[v]; } dp[u]=max(dp[u],n-num[u]);}int main(){ int a, b; while(~scanf("%d", &n)) { init(); for(int i=1; i<=n-1; i++) { scanf("%d%d", &a, &b); add(a, b); add(b, a); } dfs(1); int ans[MAX]; int k=0; for(int i=1; i<=n; i++) { if(dp[i]<=n/2) ans[++k]=i; } for(int i=1; i<=k; i++) { printf("%d\n", ans[i]); } } return 0;}a未完待续。。。。。。
阅读全文
0 0
- 树形dp小结——2
- 树形dp小结——1
- 树形dp小结
- 树形DP小结
- 基础树形DP小结
- 树形DP整理小结
- 树形DP<小小结>
- poj2342—树形dp
- 【DP_树形DP专辑小结】
- poj1463——树形DP
- poj4045——树形DP
- hdu3848——树形dp
- 树形DP——POJ3513
- HDU_1054 树形DP水题(树形DP小结)
- 树形动态规划(树状DP)小结
- 树形动态规划(树状DP)小结
- 树形——DP hdu 1520
- 树形DP——POJ1947 and POJ2486
- 在Linux下怎样让top命令启动之后就按内存使用排序(或CPU使用排序)?
- VMware12+ubuntu16+c270 linux上使用摄像头
- 双链表的尾插法建立输出
- android进阶-surfaceView的分析和使用
- ASP.NET Core 依赖注入
- 树形dp小结——2
- 1056. 组合数的和(15)
- 机器学习周志华第一章
- JavaScript学习笔记整理(八)闭包未完
- Python2和Python3的区别
- zabbix安装
- 初识JVM垃圾回收
- POJ 1700非原创
- BZOJ1042(HAOI2008)[硬币购物]--背包+容斥