问题描述
给定一棵树 T,树中每个顶点 u 都有一个权 w(u),权可以是负数。现在要找到树 T 的一个连通子图使该子图的权之和最大。
对于给定的树 T,编程计算树 T 的最大连通分支。
数据输入
第一行有1个正整数n,表示树T有n个顶点,顶点的编号为1~n
第二行有n个整数,表示顶点的权值
接下来n-1行,每行有两个整数u、v,表示顶点u与v相连
结果输出
一个整数,表示最大连通分支的权值
输入输出样例
Input Output 6 4 -1 1 3 1 -3 2 4 1 1 3 1 2 4 5 6 5
算法分析(一)
顶层问题:如何确定哪些顶点存在于最大连通子图中?
分解问题:如何求有顶点4存在的最大连通分支?
再次分解:考虑顶点4的两棵子树
扫描二维码关注公众号,回复: 9055585 查看本文章
算法分析(二)
最大连通分支在子树或树中,因此对树进行遍历,依次求出以每个结点为树根的最大连通分支权值
两个性质:最优子结构性质、子问题重叠性质
动态规划!!
算法步骤
对于叶子结点:最大连通分支的权值为该点的权值
某一结点的最大连通权值>0,则将其值加到它的父亲结点的最大连通权值,反之舍弃该值
最终求出根结点的最大连通权值,结束遍历
所求最大连通分支的权值,即为结点中最大连通权值的最大值
C++代码实现
#include <iostream>
#include <vector>
#include <list>
#include <queue>
#include <chrono>
#include <fstream>
#include <algorithm>
using namespace std;
using namespace chrono;
//邻接链表:储存无向图
vector<list<int> > adjList;
//每个结点
struct Node {
int parent; //父结点
int wMax; //以每个结点为树根的最大连通分支权值
int level; //层次
bool visit; //是否被访问过
};
Node* node = NULL;
vector<int> List;
//广度优先搜索:用于确定所有结点的level和parent
void bfs()
{
queue<int> que; //队列
que.push(1); //以结点1为树根
node[1].parent = 0;
node[1].visit = 1;
while(!que.empty())
{
int cur = que.front();
List.push_back(cur);
que.pop();
for(list<int>::iterator it = adjList[cur].begin(); it != adjList[cur].end(); ++it)
{
if(!node[*it].visit)
{
node[*it].parent = cur;
node[*it].level = node[cur].level + 1; //后代的层次 = parent层次 + 1
que.push(*it);
node[*it].visit = 1;
}
}
}
}
//自定义比较函数:按level降序排列
bool cmp(const Node& a, const Node& b) {
return a.level > b.level;
}
int main()
{
auto start = system_clock::now();
ifstream src("20.txt");
int n;
src >> n;
adjList.assign(n+1, list<int>()); //邻接表:n+1个list<int>构成的vector
node = new Node[n+1]; //n+1个
//初始化所有wMax为结点的权值
for(int i = 1; i <= n; ++i)
{
src >> node[i].wMax;
node[i].level = 0;
node[i].visit = 0;
}
//将(u, v)和(v, u)加入adjList
int u, v;
for(int i = 1; i < n; ++i)
{
src >> u >> v;
adjList[u].push_back(v);
adjList[v].push_back(u);
}
src.close();
bfs(); //广度优先搜索:用于确定所有结点的level和parent
//自底向上:依次求出以每个结点为树根的最大连通分支权值
for(int i = 0; i < n; ++i)
{
int parent = node[List[n-1-i]].parent;
if(node[i].wMax > 0)
node[parent].wMax += node[i].wMax;
}
//遍历所有结点的wMax,取最大值作为最终结果输出
int maxValue = -INT_MAX;
for(int i = 1; i <= n; ++i)
maxValue = max(maxValue, node[i].wMax);
ofstream out("result.txt");
out << maxValue;
out.close();
auto end = system_clock::now();
auto duration = duration_cast<microseconds>(end - start);
cout << "Problem size: " << n << "\nRunning time: " << double(duration.count()) * microseconds::period::num / microseconds::period::den << "s";
return 0;
}