wky1366365447

 

树形DP

在树上发生的状态转移,一般来说,状态第一维是以i为根的子树。

在Dfs的回溯部分状态转移。

 

一、树的直径

在树上随便找一点w,以w为根作Dfs或Bfs,保存距离w结点u的编号及其距离。

再以那个节点u为根,作Dfs或Bfs,找到距离u最远的结点v。

此时,u-v为树的一条直径。

 

POJ1985 Cow Marathon

这道题给出N个点,M-1条边(字符是没有用的),然后求出直径。

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<queue> 
 6 using namespace std;
 7 #define MAX_N 40010
 8 struct Edge{
 9     int to,next,value;
10 }edge[MAX_N*2];
11 int head[MAX_N];
12 int N,M,cnt,ans,node;
13 void Add(int u,int v,int w){
14     edge[++cnt].to=v;
15     edge[cnt].value=w;
16     edge[cnt].next=head[u];
17     head[u]=cnt;
18 }
19 void Bfs(int u){
20     queue<int>Q;
21     bool vis[MAX_N];memset(vis,false,sizeof(vis));
22     int dis[MAX_N];memset(dis,0,sizeof(dis));
23     Q.push(u);
24     vis[u]=true;
25     ans=0,node=u;
26     while(!Q.empty()){
27         int x=Q.front();Q.pop();
28         for(int i=head[x];~i;i=edge[i].next){
29             int v=edge[i].to,w=edge[i].value;
30             if(!vis[v]&&dis[v]<dis[x]+w){
31                 dis[v]=dis[x]+w;
32                 vis[v]=true;
33                 if(ans<dis[v]){
34                     ans=dis[v];
35                     node=v;
36                 }
37                 Q.push(v);
38             }
39         }
40     }
41 }
42 int main(){
43     while(~scanf("%d%d",&N,&M)){
44         memset(head,-1,sizeof(head));
45         memset(edge,0,sizeof(edge));
46         char str[5];
47         cnt=0;
48         for(int i=1;i<=M;i++){
49             int u,v,w;scanf("%d%d%d%s",&u,&v,&w,str);
50             Add(u,v,w);
51             Add(v,u,w);
52         }
53         Bfs(1);
54         Bfs(node);
55         printf("%d\n",ans);
56     }
57     return 0;
58 }
Bfs

 

 

 

 二、树的最大独立集

给一棵N个结点的子树,要求选出尽量多的点,使它们两两不相邻。

用树形DP的思路:状态dp(i,j)表示以i为根的子树,i结点是否被选取。

对于这颗树,dp(i,0)代表不选i这个结点,可以得出dp(i,0)=sum(max(dp(k,0),dp(k,1)))。

可以写出代码。

 

Luogu1352没有上司的舞会

这道题需要记录每个v结点的入度,入度为0的结点就是根节点。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 #define MAX_N 6010
 6 struct Edge{
 7     int to,next;
 8 }edge[MAX_N*2];
 9 int head[MAX_N],R[MAX_N],dp[MAX_N][2],_index[MAX_N];
10 int N,cnt;
11 void Add(int u,int v){
12     edge[++cnt].to=v;
13     edge[cnt].next=head[u];
14     head[u]=cnt;
15 }
16 void Dfs(int u){
17     dp[u][0]=0;
18     dp[u][1]=R[u];
19     for(int i=head[u];~i;i=edge[i].next){
20         int v=edge[i].to;
21         Dfs(v);
22         dp[u][0]+=max(dp[v][0],dp[v][1]);
23         dp[u][1]+=dp[v][0];
24     }
25 }
26 int main(){
27     memset(head,-1,sizeof(head));
28     scanf("%d",&N);
29     for(int i=1;i<=N;i++)scanf("%d",&R[i]);
30     for(int i=1;i<N;i++){
31         int u,v;scanf("%d%d",&v,&u);
32         Add(u,v);
33         _index[v]++;
34     }
35     int root=0;
36     for(int i=1;i<=N;i++){
37         if(!_index[i]){
38             root=i;
39             break;
40         }
41     }
42     Dfs(root);
43     int Max=-1;
44     for(int i=1;i<=N;i++){
45         Max=max(Max,max(dp[i][0],dp[i][1])); 
46     }
47     printf("%d",Max);
48     return 0;
49 }
Luogu1352

 

三、树形dp+01背包

只是在Dfs的回溯过程中加入了01背包的实现。

Luogu2014选课

值得注意的是,会有很多个根节点,所以把0号结点默认为根节点,所以此时的M要加1,因为0号结点是必选的。

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 #define MAX_N 310
 6 struct Edge{
 7     int to,next;
 8 }edge[MAX_N*2];
 9 int cost[MAX_N],dp[MAX_N][MAX_N],head[MAX_N];
10 int N,M,cnt;
11 void Add(int u,int v){
12     edge[++cnt].to=v;
13     edge[cnt].next=head[u];
14     head[u]=cnt;
15 } 
16 void Dfs(int u){
17     dp[u][1]=cost[u];
18     for(int i=head[u];~i;i=edge[i].next){
19         int v=edge[i].to;
20         Dfs(v);
21         for(int i=M+1;i>=1;i--){
22             for(int j=i-1;j>=1;j--){
23                 dp[u][i]=max(dp[u][i],dp[u][i-j]+dp[v][j]);
24             }
25         }
26         
27     } 
28 }
29 int main(){
30     memset(head,-1,sizeof(head));
31     scanf("%d%d",&N,&M);
32     for(int i=1;i<=N;i++){
33         int s,k;scanf("%d%d",&k,&s);
34         Add(k,i);
35         cost[i]=s;
36     }
37      Dfs(0);
38      printf("%d",dp[0][M+1]);
39     return 0;
40 }
Luou2014

 

 

四、树上次长路径

可以考虑到在一棵树上,会有一条或多条直径,所以只需要判断在当前树上,是否存在一条且仅有一条直径。

如果有,那么次长路径为直径-1,相反,路径就是直径。

需要三遍Bfs或Dfs,最后一遍是验证。

分类:

技术点:

DP

相关文章: