树形dp小结——1
来源:互联网 发布:unity3d像素游戏制作 编辑:程序博客网 时间:2024/05/23 19:44
1.通常的动态规划都是线性的,或者说是在有向无环图上进行的。而树形dp就是在树的基础上进行的dp
2.树形dp通常有两种方向,一种是自下而上,另一种是自上而下。具体利用方法根据实际要求来
叶——根,在回溯的时候从叶子节点往上更新信息
根——叶,往往是在叶——根dfs一遍以后(相当于预处理),再重新往下获取,得到的最终答案。
3.和普通dp一样,树形dp的重点在于找状态转移方程。
一.入门题
上司的舞会上司的舞会
题目大意:
如题目所言,就是自己的上司参加的话,自己就不能参加。每个节点定义一个价值,子节点和父亲节点不能同时选取。求能够选取的最大价值
题目分析:令f【u】【0】表示不选取u节点,u子树所能得到的最大价值;f【u】【1】表示选取了u节点,u子树能得到的最大价值。
在回溯的过程中通过子节点向上更新信息
设i号节点的价值为a【i】,<u,v>是一条有向边
f【u】【0】=max(f【v】【0】,f【v】【1】)
f【u】【1】=a【u】+f【v】【0】u取了,v肯定不取
void dfs(int x){ int l=tu[x].size(); for(int i=0;i<l;i++) { int t=tu[x][i]; dfs(t); dp[x][0]+=max(dp[t][1],dp[t][0]); dp[x][1]+=dp[t][0]; }}dfs(root);cout<<max(dp[root][0],dp[root][1])<<endl;
具体代码:
#include <iostream>#include <stdio.h>#include <stdlib.h>#include<string.h>#include<algorithm>#include<math.h>#include<queue>using namespace std;typedef long long ll;const int N=6010;int m,n;int dp[N][2],rd[N];vector <int>tu[N];void dfs(int x){ int l=tu[x].size(); for(int i=0;i<l;i++) { int t=tu[x][i]; dfs(t); dp[x][0]+=max(dp[t][1],dp[t][0]); dp[x][1]+=dp[t][0]; }}int main(){ while(~scanf("%d",&n)) { for(int i=1;i<=n;i++) tu[i].clear(); memset(rd,0,sizeof(rd)); for(int i=1;i<=n;i++) { scanf("%d",&dp[i][1]); dp[i][0]=0; } int x,y; while(~scanf("%d%d",&x,&y)&&x+y) tu[y].push_back(x),rd[x]++; int ans=-1; for(int i=1;i<=n;i++) if(rd[i]==0)///防止不止一个根节点 {dfs(i);ans=max(ans,max(dp[i][0],dp[i][1]));} cout<<ans<<endl; }}
入门题二:
树的重心
题目:点击打开链接
题目大意:
若树上一个节点满足其所有子树中最大子树节点数最少,则这个点即为重心
题目分析:
一个节点有多支是儿子节点,而父亲节点只有一支,题目的意思很清晰,就是在所有儿子子树中寻求一个最大的,然后跟父亲节点的一支进行比较,实际上难在理解,此题将父亲节点的那支也定义成了节点u的子树之一了。而父亲节点的那一支就用总数n减去u的子树所有的节点个数。
任选一点为根,只要统计出每个点的子树大小,就能很快求出每个点子树节点的数量最大值
求每个点子树的大小同样只需要自底向上的更新信息。记size【u】为子树节点u节点的数量(包括u本身)
size【u】=1+(size【v】的总和)
void dfs(int father,int u,int n){ int v,m,temp; for(int i=next[u];i!=-1;i=edge[i].next) { v=edge[i].v; if(v==father) continue; dfs(u,v,n);//先dfs再加 sum[u]+=sum[v]; } dp[u]=n-1-sum[u]; for(int i=next[u];i!=-1;i=edge[i].next) { v=edge[i].v; if(v==father) continue; dp[u]=max(dp[u],sum[v]); } sum[u]++;}
代码:
#include<iostream>#include<string.h>#include<algorithm>#define MAX 22000using namespace std;struct edge{ int v; int next;}edge[MAX*2];int next[MAX];int sum[MAX];int dp[MAX];int ans,n,num;void add_edge(int u,int v){ edge[num].next=next[u]; next[u]=num; edge[num++].v=v;}void dfs(int father,int u,int n){ int v,m,temp; for(int i=next[u];i!=-1;i=edge[i].next) { v=edge[i].v; if(v==father) continue; dfs(u,v,n); sum[u]+=sum[v]; } // cout<<u<<": "<<sum[u]<<endl; dp[u]=n-1-sum[u]; for(int i=next[u];i!=-1;i=edge[i].next) { v=edge[i].v; if(v==father) continue; dp[u]=max(dp[u],sum[v]); } sum[u]++;}int main(){ int t; int p,q; cin>>t; while(t--) { cin>>n; num=0; memset(sum,0,sizeof(sum)); memset(next,-1,sizeof(next)); for(int i=0;i<n-1;i++) { cin>>p>>q; add_edge(p,q); add_edge(q,p); } ans=1<<29; p=0; dfs(0,1,n); for(int i=1;i<=n;i++) { if(dp[i]<ans) { ans=dp[i]; p=i; } } cout<<p<<" "<<ans<<endl; } return 0;}
入门题3:树上最远距离
这道题最好需要画图理解。
题目大意:如题目所述,一棵无向树,辺有权值,求树上每个点能到达的最远距离
题目分析:
一个点能够走最远距离,第一步要么往儿子方向走,要么找父亲节点。
如果往儿子结点走,就会一直往儿子结点走,不存在返回的现象(以后会遇到又返回的那种题)
如果第一步往父亲节点走,那么第二步要么继续往他的父亲节点走,要么走u的兄弟子树(注意是兄弟,因为不可能回来)兄弟中的最大距离就是次最大距离的含义
%%%往儿子方向走的最远距离是很容易求出来的,而往父亲节点走的就需要自上往下推。这道题与之前两道题是不一样的,就牵扯到了从根向下传递信息。
&&&记f【u】为节点u第一步想儿子方向走的最远距离
&&&记g【u】为节点u第一步向父亲方向走的最远距离
&&&记p【u】为节点u的父亲节点的编号
&&&f【u】=max{f【v】+w(u,v)}
&&&g【u】=w(u,p【u】,max{g【p【u】】,f【v】+w(p【u】,u)})v是u的兄弟
&&&所以要两边dfs,先求出f再求出g
&&&最后节点u的最远距离为 max{f【u】,g【u】}
代码:
#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define MAXN 10200struct edge{ int to;//终端点 int next;//下一条同样起点的边号 int w;//权值}bians[MAXN*2];int tot;//总边数int head[MAXN];//head[u]=i表示以u为起点的所有边中的第一条边是 i号边void add_bian(int u,int v,int w)//添加从u->v,权值为w的边{ bians[tot].to=v; bians[tot].w=w; bians[tot].next = head[u]; head[u] = tot++;}int dist[MAXN][3];//dist[i][0,1,2]分别为正向最大距离,正向次大距离,反向最大距离int jilu[MAXN];int dfs1(int u,int fa)//返回u的正向最大距离{ if(dist[u][0]>=0) return dist[u][0]; dist[u][0]=dist[u][1]=dist[u][2]=jilu[u]=0; for(int e=head[u]; e!=-1; e=bians[e].next) { int v= bians[e].to; if(v==fa) continue; if(dist[u][0]<dfs1(v,u)+bians[e].w) { jilu[u]=v; dist[u][1] = max(dist[u][1] , dist[u][0]); dist[u][0]=dfs1(v,u)+bians[e].w; } else if( dist[u][1]< dfs1(v,u)+bians[e].w ) dist[u][1] = max(dist[u][1] , dfs1(v,u)+bians[e].w); } return dist[u][0];}void dfs2(int u,int fa){ for(int e=head[u];e!=-1;e=bians[e].next) { int v = bians[e].to; if(v==fa) continue; if(v==jilu[u])//以u为根节点的经过下一叶子的标号为v dist[v][2] = max(dist[u][2],dist[u][1])+bians[e].w;//如果是这样的话,那么就找down次最大值与up最大值的最大值 else dist[v][2] = max(dist[u][2],dist[u][0])+bians[e].w;//如果没有前边的限制,则说明还没到最后,就找down与up哪种最大 dfs2(v,u); }}int main(){ int n; while(scanf("%d",&n)==1&&n) { tot=0; memset(dist,-1,sizeof(dist)); memset(head,-1,sizeof(head)); memset(jilu,-1,sizeof(jilu)); for(int i=2; i<=n; i++) { int v,w; scanf("%d%d",&v,&w); add_bian(i,v,w);//加边 add_bian(v,i,w);//加边 } dfs1(1,-1); dfs2(1,-1); for(int i=1;i<=n;i++) printf("%d\n",max(dist[i][0],dist[i][2])); } return 0;}
- 树形dp小结——1
- 树形dp小结——2
- 树形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
- Q
- 接口和抽象类
- css3画五角星
- Android程序生成步骤
- BZOJ2438: [中山市选2011]杀人游戏
- 树形dp小结——1
- shiro学习之路-加密模块
- Python学习记录总结
- 流程控制
- PAT乙级1004. 成绩排名 (20)
- MyBatis-plus在eclipse中的使用详解
- 6.10水仙花数
- 为什么某些Win32技术在Windows NT服务中行为不当?
- 一日一文(8)