《P2700 逐个击破》
题目背景
三大战役的平津战场上,傅作义集团在以北平、天津为中心,东起唐山西至张家口的铁路线上摆起了一字长蛇阵,并企图在溃败时从海上南逃或向西逃窜。为了就地歼敌不让其逃走,指挥官制定了先切断敌人东西两头退路然后再逐个歼灭敌人的战略方针。秉承伟大军事家的战略思想,作为一个有智慧的军长你,遇到了一个类似的战场局面。
题目描述
现在有 N 个城市,其中 K 个被敌方军团占领了,N 个城市间有 N−1 条公路相连,破坏其中某条公路的代价是已知的,现在,告诉你 K 个敌方军团所在的城市,以及所有公路破坏的代价,请你算出花费最少的代价将这 K 个地方军团互相隔离开,以便第二步逐个击破敌人。
输入格式
第一行包含两个正整数 N 和 K。
第二行包含 K 个整数,表示哪个城市被敌军占领。
接下来 N−1 行,每行包含三个正整数 a,b,c,表示从 a 城市到 b 城市有一条公路,以及破坏的代价 c。城市的编号从 0 开始。
输出格式
输出一行一个整数,表示最少花费的代价。
输入输出样例
输入 #1复制
5 3 1 2 4 1 0 4 1 3 8 2 1 1 2 4 3
输出 #1复制
4
说明/提示
对于 10% 的数据,N≤10。
对于 100% 的数据,2≤N≤105,2≤K≤N,1≤c≤106。
代码实现:
#include<cstdio>
#include<algorithm>
#define MAX_NODE 100001
#define MAX_EDGE 200001
#define Loop(var, start, end) for(int var = start; var <= end; ++var)
using namespace std;
typedef long long LongLong;
// 图的相关变量
int edgeCount; // 边的数量计数器
int headNode[MAX_NODE]; // 每个节点的边链表头
int targetNode[MAX_EDGE]; // 边指向的目标节点
int nextEdge[MAX_EDGE]; // 下一条边的索引
int edgeWeight[MAX_EDGE]; // 边的权重
// 添加边的函数
inline void addEdge(int fromNode, int toNode, int weight) {
targetNode[++edgeCount] = toNode;
nextEdge[edgeCount] = headNode[fromNode];
headNode[fromNode] = edgeCount;
edgeWeight[edgeCount] = weight;
}
LongLong result; // 最终结果
bool isSpecialNode[MAX_NODE]; // 标记是否为特殊节点
// 深度优先搜索函数
// 返回值:子树中能保留的最大最小边权重
inline int dfs(int currentNode, int parentNode) {
LongLong totalSum = 0; // 子树中所有有效边的总和
LongLong maxEdge = 0; // 子树中最大的有效边
LongLong currentEdge; // 当前边的权重(取最小值)
// 遍历当前节点的所有边
for(int edgeIndex = headNode[currentNode]; edgeIndex; edgeIndex = nextEdge[edgeIndex]) {
int neighborNode = targetNode[edgeIndex];
if(neighborNode == parentNode) continue;
// 递归计算子树,取返回值和当前边权重的最小值
currentEdge = min((LongLong)dfs(neighborNode, currentNode), (LongLong)edgeWeight[edgeIndex]);
totalSum += currentEdge;
maxEdge = max(maxEdge, currentEdge); // 更新最大边
}
result += totalSum;
// 如果是特殊节点,返回一个很大的值表示这条路径需要保留
if(isSpecialNode[currentNode]) return 1e9;
// 非特殊节点,减去最大边并返回该值
result -= maxEdge;
return maxEdge;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("pro.in", "r", stdin);
freopen("pro.out", "w", stdout);
#endif
int nodeCount, specialCount;
scanf("%d%d", &nodeCount, &specialCount);
// 标记特殊节点
Loop(i, 1, specialCount) {
int node;
scanf("%d", &node);
isSpecialNode[node] = true;
}
// 读取并添加所有边
Loop(i, 1, nodeCount - 1) {
int u, v, weight;
scanf("%d%d%d", &u, &v, &weight);
addEdge(u, v, weight);
addEdge(v, u, weight);
}
// 从节点0开始深度优先搜索
dfs(0, -1);
// 输出结果
printf("%lld", result);
return 0;
}