C++深度优先(DFS)算法的应用:收集所有金币可获得的最大积分
  Gjs2egXd7m0h 2023年12月02日 23 0


涉及知识点

深度优化(DFS) 记忆化

题目

节点 0 处现有一棵由 n 个节点组成的无向树,节点编号从 0 到 n - 1 。给你一个长度为 n - 1 的二维 整数 数组 edges ,其中 edges[i] = [ai, bi] 表示在树上的节点 ai 和 bi 之间存在一条边。另给你一个下标从 0 开始、长度为 n 的数组 coins 和一个整数 k ,其中 coins[i] 表示节点 i 处的金币数量。
从根节点开始,你必须收集所有金币。要想收集节点上的金币,必须先收集该节点的祖先节点上的金币。
节点 i 上的金币可以用下述方法之一进行收集:
收集所有金币,得到共计 coins[i] - k 点积分。如果 coins[i] - k 是负数,你将会失去 abs(coins[i] - k) 点积分。
收集所有金币,得到共计 floor(coins[i] / 2) 点积分。如果采用这种方法,节点 i 子树中所有节点 j 的金币数 coins[j] 将会减少至 floor(coins[j] / 2) 。
返回收集 所有 树节点的金币之后可以获得的最大积分。
参数范围
n == coins.length
2 <= n <= 105
0 <= coins[i] <= 104
edges.length == n - 1
0 <= edges[i][0], edges[i][1] < n
0 <= k <= 104

分析

时间复杂度

O(节点数量), DFS调用的次数=节点数量*2(两种方式)*21(分割方式),当n无穷大时,2和21忽略。

核心原理

当有祖先节点现在方式二时,本节点金币会减半。由于最多有10000个金币,所以减半15次后就是0,所以减半15次以上,和减半15次结果一样。比赛时,时间紧急,所以弄了20次,避免考虑边界情况。

变量解释

m_vRet[m_iN];//m_vRet[0] 未减半各节点及子孙节点的分数 m_vRet[i] 减半i次后的最大分数

代码

核心代码

class CNeiBo2
 {
 public:
 CNeiBo2(int n, bool bDirect, int iBase = 0):m_iN(n),m_bDirect(bDirect),m_iBase(iBase)
 {
 m_vNeiB.resize(n);
 }
 CNeiBo2(int n, vector<vector>& edges, bool bDirect,int iBase=0) :m_iN(n), m_bDirect(bDirect), m_iBase(iBase)
 {
 m_vNeiB.resize(n);
 for (const auto& v : edges)
 {
 m_vNeiB[v[0]- iBase].emplace_back(v[1]- iBase);
 if (!bDirect)
 {
 m_vNeiB[v[1]- iBase].emplace_back(v[0]- iBase);
 }
 }
 }
 inline void Add(int iNode1, int iNode2)
 {
 iNode1 -= m_iBase;
 iNode2 -= m_iBase;
 m_vNeiB[iNode1].emplace_back(iNode2);
 if (!m_bDirect)
 {
 m_vNeiB[iNode2].emplace_back(iNode1);
 }
 }
 const int m_iN;
 const bool m_bDirect;
 const int m_iBase;
 vector<vector> m_vNeiB;
 };class Solution {
 public:
 int maximumPoints(vector<vector>& edges, vector& coins, int k) {
 m_iK = k;
 for (int i = 0; i < m_iN; i++)
 {
 m_vRet[i].assign(coins.size(),-1);
 }
 CNeiBo2 neiBo(coins.size(),edges, false);
 dfs(0, -1, 0, neiBo, coins);
 return m_vRet[0][0];
 }
 int dfs(int cur, const int parent, int split,const CNeiBo2& vNeiBo,const vector& coins)
 {
 if (split >= 20)
 {
 return 0;
 }
 int& iRet = m_vRet[split][cur];
 if (-1 != iRet)
 {
 return iRet;
 }
 const int curCoin = coins[cur] / (1 << split);
 int iType1 = curCoin - m_iK;
 {
 for (const auto& next : vNeiBo.m_vNeiB[cur])
 {
 if (parent == next)
 {
 continue;
 }
 iType1 += dfs(next, cur, split, vNeiBo, coins);
 }
 }
 int iType2 = curCoin/2;
 {
 for (const auto& next : vNeiBo.m_vNeiB[cur])
 {
 if (parent == next)
 {
 continue;
 }
 iType2 += dfs(next, cur, split+1, vNeiBo, coins);
 }
 }
 iRet = max(iType1, iType2);
 return iRet;
 }
 int m_iK;
 static const int m_iN = 20;
 vector m_vRet[m_iN];//m_vRet[0] 未减半各节点及子孙节点的分数 m_vRet[i] 减半i次后的最大分数
 };

测试用例

template
 void Assert(const vector& v1, const vector& v2)
 {
 if (v1.size() != v2.size())
 {
 assert(false);
 return;
 }
 for (int i = 0; i < v1.size(); i++)
 {
 assert(v1[i] == v2[i]);
 }
 }template
 void Assert(const T& t1, const T& t2)
 {
 assert(t1 == t2);
 }int main()
 {
 Solution slu;
 vector<vector> edges;
 vector coins;
 int k;
 int res;
 edges = { {0,1},{1,2},{2,3} };
 coins = { 10,10,3,3 };
 k = 5;
 res = slu.maximumPoints(edges, coins,k);
 Assert(11, res);
 edges = { {0,1},{0,2} };
 coins = { 8,4,4 };
 k = 0;
 res = slu.maximumPoints(edges, coins, k);
 Assert(16, res);//CConsole::Out(res);}


相关下载

想高屋建瓴的学习算法,请下载《闻缺陷则喜算法册》doc版

充满正能量得对大家说

闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。

墨家名称的来源:有所得以墨记之。

如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17

C++深度优先(DFS)算法的应用:收集所有金币可获得的最大积分_深度优先


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月02日 0

暂无评论

推荐阅读
Gjs2egXd7m0h