zoukankan      html  css  js  c++  java
  • 三道简单树型dp+01背包~~hdu1561,poj1947,zoj3626

    以前学树型dp就是随便的看了几道题,没有特别注意树型dp中的小分类的总结,直到上次浙大月赛一道很简单的树型dp都不会,才意识到自己太水了~~come on!

    hdu1561:题目给出了很多棵有根树,如果把每棵树的根节点都与0相连,则就是一棵完整的有根树了(N<=200),ACboy从根节点出发,他最多可以攻占m个城市,而每个城市的财富值不一定相同,问ACboy最多可以获得多少财富。就是以每个跟节点做01背包就可以了,dp[i][j]表示以i为根节点已经攻占了j个城市获得的最大财富。不过要注意一下最后要加上根节点的值,因为要求攻占了根节点才能往下攻占,当然节点0除外,具体见代码和注释:

    View Code
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<cstring>
     5 #include<algorithm>
     6 #include<vector>
     7 #define see(x) cout<<#x<<":"<<x<<endl;
     8 using namespace std;
     9 const int maxn = 205;
    10 int w[maxn], dp[maxn][maxn];
    11 vector<int> son[maxn];
    12 int n, m;
    13 void dfs(int x){
    14     int i, j, k;
    15     if(son[x].size()==0){
    16         dp[x][1] = w[x];
    17         return;
    18     }
    19     for(i=0;i<son[x].size();i++){
    20         dfs(son[x][i]);
    21     }
    22     for(i=0;i<son[x].size();i++){
    23         for(j=m;j>=0;j--){
    24             for(k=0;k<=j;k++){
    25                 if(dp[son[x][i]][k]!=-1&&dp[x][j-k]!=-1){
    26                     dp[x][j] = max(dp[x][j],dp[son[x][i]][k]+dp[x][j-k]);
    27                 }
    28             }
    29         }
    30     }
    31 /*上面就是基本树型DP加背包的基本解法了*/
    32     if(x!=0){  //x为0节点时,不需要这种要求
    33         for(j=m;j>=0;j--){//处理一下必须攻占根节点才能攻占剩下的子节点的条件,逆序正好可以处理完
    34             if(dp[x][j-1]!=-1){
    35                 dp[x][j] = dp[x][j-1]+w[x];
    36             }
    37         }
    38     }
    39 }
    40 void Init(int n){
    41     memset(dp,-1,sizeof(dp));
    42     for(int i=0;i<=n;i++){
    43         dp[i][0] = 0;
    44     }
    45     for(int i=0;i<=n;i++){
    46         son[i].clear();
    47     }
    48 }
    49 int main(){
    50     int i, j, k, l;
    51     while(~scanf("%d%d",&n,&m)&&(n||m)){
    52         Init(n);
    53         for(i=1;i<=n;i++){
    54             scanf("%d%d",&k,&w[i]);
    55             son[k].push_back(i);
    56         }
    57         w[0] = 0; dfs(0);
    58         printf("%d\n",dp[0][m]);
    59     }
    60 }

    poj1947:给出一棵有根树,问孤立出大小为P的子树要断开多少边。就是一个容量为p的背包,树型dp。dp[i][j]表示以i为根孤立出j个节点的树至少需要断开多少条边,于是dp[x][j] = min{dp[x1][j1]+dp[x2][j2]+dp[x3][j3]+……+dp[xn][jn]},j1+j2+j3+……+jn = j,但是也不是完全这样了,其实在对每个背包进行更新时,应该是dp[x][j] = min{dp[x][j],dp[son[x][i]][k]+dp[x][j-k]-2},减2是因为要减去算了两次的x到son[x][i]的那条边,具体见代码,写的挺纠结的。

    View Code
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cmath>
     5 #include<cstring>
     6 #include<vector>
     7 using namespace std;
     8 const int maxn = 155;
     9 vector<int> son[maxn];
    10 bool vis[maxn];
    11 int dp[maxn][maxn];
    12 int n, p;
    13 void dfs(int root, int x){
    14     int i, j, k;
    15     if(x==root) dp[x][1] = son[x].size();
    16     else dp[x][1] = son[x].size()+1;
    17 
    18     for(i=0;i<son[x].size();i++){
    19         dfs(root,son[x][i]);
    20     }
    21     for(i=0;i<son[x].size();i++){
    22         for(j=p;j>=0;j--){
    23             for(k=0;k<=j;k++){
    24                 if(dp[x][k]<maxn&&dp[son[x][i]][j-k]<maxn){
    25                     dp[x][j] = min(dp[x][j],dp[x][k]+dp[son[x][i]][j-k]-2);
    26                 }
    27             }
    28         }
    29     }
    30 }
    31 void Init(int n){
    32     int i, j;
    33     for(i=0;i<=n;i++){
    34         for(j=0;j<=n;j++){
    35             dp[i][j] = maxn;
    36         }
    37         son[i].clear();
    38         vis[i] = 0;
    39     }
    40 }
    41 int main(){
    42     int i, j, k, x1, x2, ans;
    43     while(~scanf("%d%d",&n,&p)){
    44         Init(n);
    45         for(i=0;i<n-1;i++){
    46             scanf("%d%d",&x1,&x2);
    47             son[x1].push_back(x2);
    48             vis[x2] = 1;
    49         }
    50         for(i=1;i<=n;i++){
    51             if(!vis[i]){
    52                 dfs(i,i);break;
    53             }
    54         }
    55         ans = maxn;
    56         for(i=1;i<=n;i++){
    57             ans = min(ans,dp[i][p]);
    58         }
    59         printf("%d\n",ans);
    60     }
    61 }

    zoj3626:这个就是上次浙大月赛的题了,题意跟hdu1561类似,给出一棵树,n-1条边有不同的边全,n个城市有不同的财富,一个人最多只可以走m/2长度,为最多可以占领多少财富。dp[i][j]表示以i为根继续走了j个长度可以获得的最大财富,自己就是在刷完hdu1561之后简单的改改顺利的a了,两道题可能可以改成一样的写法,不过想到怎么写,就怎么写了

    View Code
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<cstring>
     5 #include<algorithm>
     6 #include<vector>
     7 #define see(x) cout<<#x<<":"<<x<<endl;
     8 using namespace std;
     9 const int maxn = 110;
    10 vector<int> son[maxn], w[maxn];
    11 int val[maxn];
    12 int dp[maxn][205];
    13 int n, m;
    14 void dfs(int x, int pre){
    15     int i, j, k;
    16     if(son[x].size()==1&&pre==son[x][0]){
    17         return;
    18     }
    19     for(i=0;i<son[x].size();i++){
    20         if(pre!=son[x][i]){
    21             dfs(son[x][i],x);
    22         }
    23     }
    24     for(i=0;i<son[x].size();i++){
    25         if(pre!=son[x][i]){
    26             for(j=m-w[x][i];j>=0;j--){
    27                 for(k=0;k<=j;k++){
    28                     if(dp[son[x][i]][k]!=-1&&dp[x][j-k]!=-1){
    29                         dp[x][j+w[x][i]] = max(dp[x][j+w[x][i]],dp[son[x][i]][k]+dp[x][j-k]);
    30                     }
    31                 }
    32             }
    33         }
    34     }
    35 }
    36 void Init(int n){
    37     memset(dp,-1,sizeof(dp));
    38     for(int i=0;i<=n;i++){
    39         son[i].clear();
    40         w[i].clear();
    41     }
    42 }
    43 int main(){
    44     int i, j, k, x1, x2, l, ans;
    45     while(~scanf("%d",&n)){
    46         Init(n);
    47         for(i=1;i<=n;i++){
    48             scanf("%d",&val[i]);
    49             dp[i][0] = val[i];
    50         }
    51         for(i=0;i<n-1;i++){
    52             scanf("%d%d%d",&x1,&x2,&l);
    53             son[x1].push_back(x2);w[x1].push_back(l);
    54             son[x2].push_back(x1);w[x2].push_back(l);
    55         }
    56         scanf("%d%d",&k,&m);
    57         m/=2;dfs(k,-1);
    58         ans = val[k];
    59         for(i=0;i<=m;i++){
    60             ans = max(ans,dp[k][i]);
    61         }
    62         printf("%d\n",ans);
    63     }
    64 }

     按理说dp都没啥模板,但是其实树上dp+背包,如果不复杂的化,也有点按部就班的感觉了~下次想出状态,寻轨道距应该就能做出来了。

  • 相关阅读:
    python unittest--TestSuit类--通过unittest.TestSuite()类直接构建,或者通过TestSuite实例的addTests、addTest方法构建
    Cannot read property 'toLowerCase' of undefined
    Vue 中登录功能的简单实现
    git 常用命令
    js 锚点定位的简单方法
    Vue element-ui 使用Loading服务按需引入的坑
    防抖 节流
    element-ui 日期选择器-开始结束日期选择限制
    vue elment-ui 对navTab导航组件封装
    vue 监听窗口变化对页面部分元素重新渲染
  • 原文地址:https://www.cnblogs.com/celia01/p/2619063.html
Copyright © 2011-2022 走看看