自己独立思考出这道题, 而且正解思路是一遍过, so 嗨皮.
贴一下思路历程, 思考的过程是很珍贵的. 对一个问题, 从陌生到有一个大致的了解, 其中会有错误的思路, 发现错误然后及时回头去想其他的方法. 有时有能做出结果的思路但不是最好的思路, 从差方法上进行横向拓展, 突然灵机一动, 想到另一种方法, 最后解答出来.
以下
初印象: 这像是一道DAG最长路或者说关键路径题目,但是又很大的不同.
因为不用修建所有隧道,所以正确的路径肯定是一条能够通往终点的无分支路径,答案就是所有这样路径里面需要时间最短的
- 那么有两种情况
-
- 这条路径可以根据某种规则直接选出来,然后求它的最短时间即可
- 不选出这条路径,而是遍历所有的这样的路径,求出其中的最短时间的最短的
看题目数据正解大概O(nlogn)一级别的复杂度
其中60%的数据可以是O(n^2)的时间复杂度.
先来考虑这个问题:有这样一条路径,如何确定修建它的最短时间?
—等等,似乎上面全错.一个公司只能修建一条隧道, 而且有n个公司那么足够修一条路了. 公司是不用选的.
只需要选择路径,并且路径里面边长最大的就是结果.
那么岂不是很简单?只要比较所有可达路径里面边数<=n的,其中最大耗时最小的就是结果了.
那么最优解可能是有一条边数>=n的路径嘛? 可以证明是不会的.因为会成环,成环就没有意义了. 所以不用担心人手不够
那么这些路, 每个顶点只会经过一次,可以方便地用dfs去遍历了. 每次步进到新的点,比较一下最大边,找出最小就ok
暴力DFS的程序提交了,TLE20分, 这果然不像单纯地一张图上对所有边进行遍历, 毕竟是会恢复状态的,这就是我之前潜意识里面认为的那种DFS是时间复杂度很高的情况.
不过思路应该没错,那么,是不是记忆化搜索呢?
是不是记忆化搜索我不知道,但我发现它可能是最小生成树!!!
先找到最小生成树路径, 然后依次把大的边删掉,看1是否还和n连通,当遇到第一个不能删的边时,这个就是答案了.
amazing, 因为它可以修n条路,那么铁定的最小生成树的n - 1条边都可以修好,那么1-n肯定是联通的.
既然如此,那就从树里面找出满足连通性质的最小最大边,根据贪心的思想,当然是从最大边开始删,而且他的短边
对结果是没有影响的,反正人手充足.那么用Kruskal的话,这部分是O(MlogM).
接下来的问题是: 如何删边.
又思考一下,发现与其删边,不如加边,直到加到第一个满足1和n连通的边.
perfect.
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
struct Edge {
int from, to, w;
Edge(int f, int t, int w) : from(f), to(t), w(w) {}
};
const int maxn = 100000 + 5;
int n, m, father[maxn];
vector<Edge> edge, edg;
bool cmp(Edge x, Edge y)
{
return x.w < y.w;
}
int find(int x)
{
return x == father[x] ? x : father[x] = find(father[x]);
}
void Kruskal()
{
sort(edge.begin(), edge.end(), cmp);
for (int i = 0; i < edge.size(); ++i) {
int x = find(edge[i].from), y = find(edge[i].to);
if (x != y) {
father[x] = y;
edg.push_back(edge[i]);
if (edg.size() == n - 1) return;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; ++i)
father[i] = i;
for (int i = 0, from, to, w; i < m; ++i) {
cin >> from >> to >> w;
edge.push_back(Edge(from, to, w));
}
Kruskal();
for (int i = 1; i <= n; ++i)
father[i] = i;
for (int i = 0; i < edg.size(); ++i) {
int x = find(edg[i].from), y = find(edg[i].to);
if (x != y) {
father[x] = y;
}
if (find(1) == find(n)) {
cout << edg[i].w;
return 0;
}
}
}