• 刷题总结——探险(ssoj)


    题目:

    国家探险队长 Jack 意外弄到了一份秦始皇的藏宝图,于是,探险队一行人便踏上寻宝之旅,去寻找传说中的宝藏。

    藏宝点分布在森林的各处,每个点有一个值,表示藏宝的价值。它们之间由一些小路相连,小路不会形成环,即两个藏宝点之间有且仅有一条道路。探险队从其中的一点出发,每次他们可以留一个人在此点开采宝藏,也可以不留,然后其余的人可以分成若干队向这一点相邻的点走去。需要注意的是,如果他们把队伍分成两队或两队以上,就必须留一个人在当前点,提供联络和通讯,当然这个人也可以一边开采此地的宝藏。并且,为了节约时间,队伍在前往开采宝藏过程中是不会走回头路的。现在你作为队长的助理,根据已有的藏宝图,请计算探险队所能开采的最大宝藏价值。

    注意:在整个过程中,每个人最多只能开采一个点的宝藏。

    输入格式

    第 1 行有 2 个整数 n 和 m。其中 n 表示藏宝点的个数(1≤n≤100),m 表示探险队的人数(1≤m≤100)。

    第 2 行是 n 个不超过 100 的整数,分别表示 1 到 n 每个点的宝藏价值。

    接下来 n-1 行,每行两个数,x 和 y(1≤x,y≤n,x≠y),表示藏宝点 x 与 y 之间有一条路,数据保证不会有重复的路出现。

    假设一开始探险队在点 1 处。

    输出格式

    输出一个整数,表示探险队所能获得最大宝藏价值。

    样例数据 1

    输入  [复制]

    5 3 
    1 3 7 2 8 
    1 2 
    2 3 
    1 4 
    4 5

    输出

    16

    备注

    【数据范围】
    对 40% 的输入数据 :1≤n≤30;m≤12。
    对 100% 的输入数据 :1≤n≤100;m≤100。

    题解:

      树形dp··多叉树转二叉树处理附加维分配问题···很像选课··不过注意dp的一些细节··

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<ctime>
    #include<cctype>
    #include<string>
    #include<cstring>
    #include<string>
    #include<algorithm>
    using namespace std;
    const int N=105;
    int father[N],son[N],brother[N],f[N][N][2],first[N],next[N*2],go[N*2],tot,n,m,val[N];
    inline int R()
    {
      char c;int f=0;
      for(c=getchar();c<'0'||c>'9';c=getchar());
      for(;c<='9'&&c>='0';c=getchar())  f=(f<<3)+(f<<1)+c-'0';
      return f;
    }
    inline void comb(int a,int b)
    {
      next[++tot]=first[a],first[a]=tot,go[tot]=b;
      next[++tot]=first[b],first[b]=tot,go[tot]=a;
    }
    inline void dfs(int u,int fa)
    {
      for(int e=first[u];e;e=next[e])
      {
        int v=go[e];if(v==fa)  continue;
        father[v]=u;dfs(v,u);
      }
    }
    inline void dp(int u,int k,int t)
    {
      if(f[u][k][t]!=-1)  return; 
      if(u==0||k==0)  {f[u][k][t]=0;return;}
      f[u][k][t]=0;
      if(t==1)  //只有父亲节点有人驻守才能分开 
      {
        for(int i=0;i<k;i++)   //该节点留一个人    
        {
          dp(son[u],i,1),dp(brother[u],k-i-1,1);f[u][k][t]=max(f[u][k][t],f[son[u]][i][1]+f[brother[u]][k-i-1][1]+val[u]);
        }
        for(int i=0;i<=k;i++)  //该节点不留人 
        {
          dp(son[u],i,0),dp(brother[u],k-i,1);f[u][k][t]=max(f[u][k][t],f[son[u]][i][0]+f[brother[u]][k-i][1]);
        }
      }
      else  //否则只能儿子节点走完或者兄弟节点走完 
      { 
        dp(son[u],k,0);dp(son[u],k-1,1);dp(brother[u],k,0);
        f[u][k][t]=max(f[u][k][t],max(f[son[u]][k][0],f[brother[u]][k][0]));
        f[u][k][t]=max(f[u][k][t],f[son[u]][k-1][1]+val[u]);
      }
      return;
    }
    int main()
    {
      //freopen("a.in","r",stdin);
      memset(f,-1,sizeof(f));
      n=R(),m=R();int a,b;
      for(int i=1;i<=n;i++)  val[i]=R();
      for(int i=1;i<n;i++)  a=R(),b=R(),comb(a,b);
      dfs(1,0);for(int i=1;i<=n;i++)  brother[i]=son[father[i]],son[father[i]]=i;
      dp(son[0],m,0);
      cout<<f[son[0]][m][0]<<endl;
      return 0;
    }
  • 相关阅读:
    TCP的流量控制
    [数组]数组元素分割
    [折半查找]排序数组中某个元素出现次数
    [队列]判断出栈序列
    [排序算法]堆排序
    [树结构]有实际用途的树的计算公式
    重写重要的库函数
    [链表]同时遍历两个链表
    [查找]二分查找
    [数组]数组元素置换方法
  • 原文地址:https://www.cnblogs.com/AseanA/p/7728742.html
Copyright © 2020-2023  润新知