[31]周年庆-拼多多2018秋

1.题目描述

拼多多王国的城市和道路的拓扑结构比较特别,是一个树状结构:

  1. 每个城市是树的一个节点;
  2. 城市之间的道路是树的一条边;
  3. 树的根节点是首都。

拼多多周年庆马上就要到了,这是拼多多王国的一个大日子。为了活跃气氛,国王想在道路上布置花灯。花灯可是很贵的东西,尽管国王想要在所有道路上都布置花灯,但是如果要花太多钱的话,是过不了财政大臣那一关的。国王把这个计划告诉财政大臣,最后他们商讨出来这么一个方案:

  1. 一条道路要么不布置花灯,要么整条布置花灯,不能选择其中的某一段布置;
  2. 除非没有道路通向首都,否则至少为一条通向首都的道路布置花灯;
  3. 所有布置花灯的道路构成的子图是连通的,这保证国王从首都出发,能通过只走布置了花灯的道路,把所有的花灯游览完;
  4. 如果某个城市(包括首都)有大于等于2条道路通向子城市,为了防止铺张浪费,最多只能选择其中的两条路布置花灯;
  5. 布置花灯的道路的总长度设定一个上限。

在上述方案下,国王想要使得布置花灯的道路长度越长越好,你帮国王想想办法。

  • 输入描述:
    每个测试输入包含1个测试用例。 输入的第一行是一个正整数m,0<m<=9900,表示布置花灯的道路的总长度的上限。 输入的第二行是一个正整数nn<=100,表示城市的个数。 紧接着是n-1行输入,每行三个正整数uvd,表示下标为u的城市有一条长度为d的道路通向它的一个子城市v,其中 0<=u<n,0<=v<n,0<d<=100。
  • 输出描述:
    输出一个正整数,表示能布置花灯的道路长度的最大值
  • 输入示例:
    5
    5
    0 1 1
    0 2 2
    0 3 3
    0 4 4
    
  • 输出示例:
    5
    

2.题目解析

dfs搜索所有可能路径的长度值。包括一个节点所有的儿子中选1个儿子,选2个儿子的情况。
后序深度遍历

建表,把所有的情况统计出来。

本题可以通过树形动态规划的方法去做,具体算法过程是:

  1. 先对所有节点排一个拓扑序,保证对于每个节点,它的父节点都排在它后面,这个排序可以通过一次树的后序遍历得到;
  2. 对每个节点,记录一个集合SS存的是以当前节点作为根节点的子树下,所有方案的总长度的所有可能值;
  3. 按照步骤1得到的拓扑序遍历所有节点,对每个节点u,假设它的集合为Su,我们枚举三种情况:
    a) 一个子节点都不选,只要往Su集合里面插入0即可;
    b) 只选择一个子节点,则枚举所有子节点,假设某个子节点当前记录的集合为Sv,道路长度为d,那 么对Sv里面的所有元素都加上d,插入到Su
    c) 选择两个子节点,则枚举所有两个子节点的组合,类似3a的做法,把所有可能性插入到Su
  4. 最后看一下根节点中的S集合里面小于等于上限值m的最大值就是答案。

3.参考答案

#include <bits/stdc++.h>
using namespace std;
struct ChildInfo{
  int child;    // 子城市下标
  int distance; // 到子城市的距离
};

void dfs(vector<vector<ChildInfo> > &lnk, int u, vector<int> &seq) {
  for (int k = 0; k != lnk[u].size(); ++k) {
    dfs(lnk, lnk[u][k].child, seq);
  }
  seq.push_back(u);
}
int main() {
  int m = 0; // 花灯道路总长度上限
  int n = 0; // 城市个数
  while (scanf("%d%d", &m, &n) == 2) {
    vector<vector<ChildInfo> > lnk(n); // 下标是父城市,元素是子程序与距离数组
    vector<int> in(n, 0);// 子城市统计
    for (int i = 1; i < n; ++i) {
      int u = 0; // 父城市
      int v = 0; // 子城市
      int d = 0; // 距离
      scanf("%d%d%d", &u, &v, &d);
      in[v]++;
      // 构建树
      lnk[u].push_back(ChildInfo{v, d});
    }
    
    // 查找根节点
    int root; // 根节点
    for (int i = 0; i < n; ++i) {
      if (in[i] == 0) {
        root = i;
        break;
      }
    }
      
    // 后序遍历,排列城市顺序
    vector<int> seq;
    dfs(lnk, root, seq);
    
    // 统计
    vector<int> mem[n];
    for (int i = 0; i != n; ++i) {
      int u = seq[i];
      // 第一种情况,不选择
      mem[u].push_back(0);
      // 第二种情况,选择一个子城市
      for (int k = 0; k != lnk[u].size(); ++k) {
        int v = lnk[u][k].child;
        int dist = lnk[u][k].distance;
        vector<int> memv = mem[v];
        for (auto &value : memv) {
            int d = value + dist;
            if(mem[u].end() == find(mem[u].begin(),mem[u].end(),d)){
                mem[u].push_back(d);
            }
        }
      }
      // 第三种情况,选择两个子城市
      for (int k1 = 0; k1 != lnk[u].size(); ++k1)
        for (int k2 = k1 + 1; k2 != lnk[u].size(); ++k2) {
          int v1 = lnk[u][k1].child;
          int dist1 = lnk[u][k1].distance;
          vector<int> memv1 = mem[v1];
          int v2 = lnk[u][k2].child;
          int dist2 = lnk[u][k2].distance;
          vector<int> memv2 = mem[v2];
          for (auto &value1 : memv1)
            for (auto &value2 : memv2) {
              int d = value1 + dist1 + value2 + dist2;
              if(mem[u].end() == find(mem[u].begin(),mem[u].end(),d)){
                mem[u].push_back(d);
              }
            }
        }
    }
    // 找到满足条件的最大值
    int result = 0;
    for (auto &value : mem[root])
      if (value <= m) { // 必须小于花灯道路总长度上限
        result = max(result, value);
      }
    printf("%d\n", result);
  }
  return 0;
}

牛课题目

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容