当前位置: 首页 > news >正文

【题解】Luogu P8137 [ICPC2020 WF] ’S No Problem

题目大意

给定一个无向树。有两个扫雪机可以任意选择出发终止点,可以走回头路。求两个扫雪机遍历整个树所需的最短路程。

解题思路

不妨先考虑只有一个扫雪机的情况。

我们知道,在无向树上从任意一个结点出发,将所有的边经过两遍,串成一个环,最终都能回到那个结点。就像这样:

这也就是这一个扫雪机遍历整个树的最坏情况。

但是所有的路我们都走了两遍,很明显我们并不需要这样做,也不需要回到起始节点。于是我们将这个环去除一条路径,剩下的边就构成了一条简单路径,也就是起点和终点不在一个点上的扫雪机路线。并且这个去除的路径一定不能同时包含原图中一条边的两个方向,否则扫雪机就不能到达所有结点。

那么,自然是去除的路径越长,扫雪机所需走的路程就越短。

显然,要去除的就是树的直径。去除后的路径就是一台扫雪遍历完整棵树所需最短路径。就像这样:

接下来就很好办了。既然一台扫雪机的情况我们已经得出,那么将其推广到两台扫雪机甚至 \(k\) 台扫雪机就都不是问题。

上面得出,去除一条最长路径就是一台扫雪机的最短路径,那么去除两条不相交的最长路径就是两台扫雪机的最短路径(相交了就同时去除了原图一条边的两个方向),去除 \(k\) 条不相交最长路径就是 \(k\) 台扫雪机的最短路径。

由此,解法已经得出。

代码实现

显然使用树形DP求解。

深搜这棵树,记录每个结点 \(u\) 的四个状态:

  • \(f_1\):以 \(u\) 为根的子树中,往 \(u\) 结点之上延伸的最长的一条路径
  • \(f_2\):以 \(u\) 为根的子树中,不往 \(u\) 结点之上延伸的最长的一条路径
  • \(f_3\):以 \(u\) 为根的子树中,往 \(u\) 结点之上延伸(当然只能延伸一条)的两条路径
  • \(f_4\):以 \(u\) 为根的子树中,不往 \(u\) 结点之上延伸的两条路径

如果是 \(k\) 台扫雪机,那么就继续往下以此类推。整棵树根 \(root\) 的第 \(2\times k\) 个状态即为 \(k\) 条不相交最长路径的长度和。

不过这些状态之间转移的关系错综复杂,并且对着方程仔细琢磨一下也好理解,于是这里就把所有的方程都列出来(这里 \(u\) 表示子树根节点,\(v\) 表示 \(u\) 的子结点,\(w\) 表示 \(u\)\(v\) 之间边的边权):

\[f_4(u)=\max(f_4(u),f_3(u)+f_1(v)+w) \]

\[f_4(u)=\max(f_4(u),f_2(u)+f_2(v)) \]

\[f_3(u)=\max(f_3(u),f_2(u)+f_1(v)+w) \]

\[f_4(u)=\max(f_4(u),f_1(u)+f3(v)+w) \]

\[f_3(u)=\max(f_3(u),f_1(u)+f_2(v)) \]

\[f_2(u)=\max(f_2(u),f_1(u)+f_1(v)+w) \]

\[f_4(u)=\max(f_4(u),f_4(v)) \]

\[f_3(u)=\max(f_3(u),f_3(v)+w) \]

\[f_2(u)=\max(f_2(u),f_2(v)) \]

\[f_1(u)=\max(f_1(u),f_1(v)+w) \]

这些方程单独写出来固然清晰明白好理解,但是在敲代码的时候非常容易出错。因此 ICPC judge bruce merry 有一份非常巧妙的代码,使用一个双层循环来完成状态的转移,并且可以直接通过修改 \(E\) 的值来求出任意 \(k\) 条不相交最长路径。贴在这里:

static vector<int> recurse(vector<node> &nodes, int cur, int parent, int E) {node &n = nodes[cur];vector<int> dp(E + 1, 0);for (const auto &ch : n.edges) {if (ch.dest == parent) continue;auto sub = recurse(nodes, ch.dest, cur, E);for (int i = E-1; i >= 0; i--)for (int j = E - i; j >= 1; j--) {int score = dp[i] + sub[j] + ((j & 1) ? ch.len : 0);dp[i + j] = max(dp[i + j], score);}}return dp;
}

最后输出二倍边权和减去除的路径总长即可求出答案。

完整代码:

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=1e5+10;
struct Node{int to,nxt,w;
}e[2*maxn];
int n,sum,ans;
int h[maxn],tot;
int f1[maxn],f2[maxn],f3[maxn],f4[maxn]; 
void Add(int u,int v,int w){tot++;e[tot].to=v;e[tot].nxt=h[u];e[tot].w=w;h[u]=tot;
} 
void Dfs(int root,int fa){for(int i=h[root];i;i=e[i].nxt){int v=e[i].to;if(v==fa) continue;Dfs(v,root);f4[root]=max(f4[root],f3[root]+f1[v]+e[i].w);f4[root]=max(f4[root],f2[root]+f2[v]);f3[root]=max(f3[root],f2[root]+f1[v]+e[i].w);f4[root]=max(f4[root],f1[root]+f3[v]+e[i].w);f3[root]=max(f3[root],f1[root]+f2[v]);f2[root]=max(f2[root],f1[root]+f1[v]+e[i].w);f4[root]=max(f4[root],f4[v]);f3[root]=max(f3[root],f3[v]+e[i].w);f2[root]=max(f2[root],f2[v]);f1[root]=max(f1[root],f1[v]+e[i].w);}
}
int main(){scanf("%d",&n);for(int i=1;i<=n-1;i++){int a,b,c;scanf("%d%d%d",&a,&b,&c);Add(a,b,c);Add(b,a,c);sum+=2*c;}Dfs(1,0);ans=f4[1];cout<<sum-ans;return 0;
} 

注意事项

无向边开二倍数组。

http://www.jsqmd.com/news/79194/

相关文章:

  • Day 36 官方文档的阅读
  • 青少年编程学习:考级与竞赛如何平衡
  • 2025 Autel MaxiVCI V150 Wireless Dongle: CAN FD/DOIP for Autel 900 Series Scanners
  • 【UI Qt】入门笔记
  • WSL安装方法
  • Ubuntu环境中LLaMA Factory 的部署与配置—构建大语言模型微调平台 - 实践
  • 2025贵阳公墓/公益公墓top5推荐!贵阳优质生态陵园榜单发布,合规保障与人文关怀兼具的安息之所推荐 - 全局中转站
  • 【题解】Luogu P2354 [NOI2014] 随机数生成器
  • 基于Django的农场管理系统
  • rsync交叉编译步骤
  • 下载UCI数据集《Secondary Mushroom》
  • 【题解】P11453 [USACO24DEC] Deforestation S
  • 03 以上版本 Excel 文件解压替换图片
  • 【题解】Luogu P13977 数列分块入门 2
  • AI核心知识50——大语言模型之Scaling Laws(简洁且通俗易懂版)
  • MySQL 深分页查询优化实践与经验总结
  • P2014 [CTSC1997] 选课
  • 彻底讲清 MySQL InnoDB 锁机制:从 Record 到 Next-Key 的全景理解
  • 超越宣传:基于数据与案例的软件人才外包服务商价值评估指南
  • MCU的启动流程你了解么?
  • 电机多目标优化与灵敏度分析:探索电机性能提升之道
  • I2C通信最全面的讲解:从协议到硬件设计
  • 打造下一个爆款!专业短剧APP全栈开发解决方案,解锁万亿级市场红利
  • 毕业论文选题AI推荐:9大工具+热门方向合集
  • 【题解】Luogu P10752 [COI 2024] Sirologija
  • PFC2D预制裂隙巴西劈裂试验模拟:探索岩石破裂奥秘
  • Python字符串:别只用来打印!这5个高级用法让代码效率翻倍
  • PSRR仿真教程:解锁电路抗噪能力的密钥
  • C51_AH3144霍尔传感器
  • C51_74HC595串口转并口