今日刷题:\(1009-1012\)
1009 NC53074 Forsaken喜欢独一无二的树
题目描述
众所周知,最小生成树是指使图中所有节点连通且边权和最小时的边权子集。
不过最小生成树太简单了,我们现在来思考一个稍微复杂一点的问题。
现在给定一个 \(n\) 个点,\(m\) 条边的图,每条边 \(e_i\) 都有一个权值 \(w_i\)。定义删除一条边 \(e_i\) 的代价为 \(w_i\),并且你可以对这个图执行任意次删边操作。
设这个图的最小生成树权值和为 \(sum\),定义一个图的最小生成树是独一无二的当且仅当这个图的边集中没有除最小生成树外的其他子集能满足权值和为 \(sum\) 且使得所有点连通。一个图刚开始可能没有独一无二的最小生成树,现在你可以删除一些边,使得剩下的边的最小生成树大小依然为 \(sum\) 并且这个图的最小生成树是独一无二的。
现在我们想要知道删除的边的权值和最小是多少?
输入描述
第一行输入为 \(n\) 和 \(m\),表示这个图的点数和边数。
接下来 \(m\) 行,每行三个值 \(u_i,v_i,w_i\),分别代表每条边的两个端点和边权。
输出描述
一个整数,代表删除的边的最小权值和。
示例
输入
1 0
输出
0
备注
\(1≤n≤2e^5,n−1≤m≤2e^5,1≤u_i,v_i≤n,1≤w_i≤1e^9\)
解题思路
要保证剩下的边构成最小生成树且该最小生成树是独一无二的,则需要在构成最小生成树的情况下,删除多余的边。
那么何时有多个最小生成树呢?举个例子:假设点为 \(1-3\),边为 \(1-2,2-3,3-1\),所有边的权值均为 \(1\),则其有三个最小生成树。
因此在 \(Kruskal\) 算法的基础上,先将所有权值相同的边权值加上,然后删除会使得图中形成环的边即可。代码如下所示,先将可能构成最小生成树的边加上,然后删除多余的边。
for (int k = i; k < j; k++) {
int u = edges[k].u, v = edges[k].v;
if (find(u) != find(v))
res += (LL) edges[i].w;
}
for (int k = i; k < j; k++) {
int u = edges[k].u, v = edges[k].v;
if (find(u) != find(v)) {
p[find(u)] = find(v);
res -= (LL) edges[i].w;
}
}
C++ 代码
// NC53074
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
typedef long long LL;
int n, m;
struct Edge {
int u, v, w;
bool operator< (const Edge &E) {
return w < E.w;
}
} edges[N];
int p[N];
int find(int x) {
if (p[x] != x)
p[x] = find(p[x]);
return p[x];
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
p[i] = i;
for (int i = 0; i < m; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
edges[i] = {u, v, w};
}
sort(edges, edges + m);
LL res = 0;
for (int i = 0; i < m; ) {
int j = i;
while (edges[j].w == edges[i].w && j < m)
j++;
for (int k = i; k < j; k++) {
int u = edges[k].u, v = edges[k].v;
if (find(u) != find(v))
res += (LL) edges[i].w;
}
for (int k = i; k < j; k++) {
int u = edges[k].u, v = edges[k].v;
if (find(u) != find(v)) {
p[find(u)] = find(v);
res -= (LL) edges[i].w;
}
}
i = j;
}
printf("%lld\n", res);
return 0;
}
1010 NC15108 道路建设
题目描述
随着如今社会的不断变化,交通问题也变得越来越重要,所以市长决定建设一些公路来方便各个城市之间的贸易和交易。虽然市长的想法很好,但是他也遇到了一般人也经常头疼的问题,那就是手头的经费有限……在规划过程中,设计师们已经预算出部分城市之间建设公路的经费需求。现在市长想知道,它能不能将他的 \(m\) 个城市在有限的经费内实现公路交通。如果可以的话,输出 Yes,否则输出 No(两个城市不一定要直接的公路相连,间接公路到达也可以。)
输入描述
测试输入包含多条测试数据
每个测试数据的第 \(1\) 行分别给出可用的经费 \(c(<1000000)\),道路数目 \(n(n<10000)\),以及城市数目 \(m(<100)\)。
接下来的 \(n\) 行给出建立公路的成本信息,每行给出三个整数,分别是相连的两个城市 \(v_1\)、\(v_2(0<v_1,v_2<=m)\) 以及建设公路所需的成本 \(h(h<100)\)。
输出描述
对每个测试用例,输出 Yes 或 No。
示例1
输入
20 10 5
1 2 6
1 3 3
1 4 4
1 5 5
2 3 7
2 4 7
2 5 8
3 4 6
3 5 9
4 5 2
输出
Yes
示例2
输入
10 2 2
1 2 5
1 2 15
输出
Yes
备注
两个城市之间可能存在多条线路
解题思路
最小生成树模板题,将最小代价和经费作比较即可。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 110, M = 10010;
int n, m, c, p[N];
struct Edge {
int a, b, w;
bool operator< (const Edge &E) {
return w < E.w;
}
} edges[M];
int find(int x) {
if (p[x] != x)
p[x] = find(p[x]);
return p[x];
}
int main() {
while (~scanf("%d%d%d", &c, &m, &n)) {
for (int i = 1; i <= n; i++)
p[i] = i;
for (int i = 0; i < m; i++) {
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
edges[i] = {a, b, w};
}
sort(edges, edges + m);
int cnt = 0, sum = 0;
for (int i = 0; i < m; i++) {
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
int pa = find(a), pb = find(b);
if (pa != pb) {
p[pa] = pb;
cnt++;
sum += w;
}
if (cnt == n - 1)
break;
}
if (sum > c)
puts("No");
else
puts("Yes");
}
return 0;
}
1011 NC52867 Highway
题目描述
In ICPCCamp there were \(n\) towns conveniently numbered with \(1,2,…,n\) connected with (\(n - 1\)) roads. The \(i\)-th road connecting towns \(a_i\) and \(b_i\) has length \(c_i\).
It is guaranteed that any two cities reach each other using only roads.
Bobo would like to build (\(n - 1\)) highways so that any two towns reach each using only highways.
Building a highway between towns x and y costs him \(δ(x,y)\) cents, where \(δ(x,y)\) is the length of the shortest path between towns \(x\) and \(y\) using roads.
As Bobo is rich, he would like to find the most expensive way to build the (\(n - 1\)) highways.
输入描述
The input contains zero or more test cases and is terminated by end-of-file. For each test case:
The first line contains an integer \(n\).
The \(i\)-th of the following (\(n - 1\)) lines contains three integers \(a_i,b_i\) and \(c_i\).
备注
\(1≤n≤10^5,1≤a_i,b_i≤n,1≤c_i≤10^8\).
The number of test cases does not exceed \(10\).
输出描述
For each test case, output an integer which denotes the result.
示例
输入
5
1 2 2
1 3 1
2 4 2
3 5 1
5
1 2 2
1 4 1
3 4 1
4 5 2
输出
19
15
解题思路
树上距离完全图的最大生成树,其实就是每个点连到直径更远的端点。
反证:假设点 \(i\) 到两个端点 \(s,t\) 的距离分别是 \(dist[s,i]\) 和 \(dist[i,t]\),且 \(dist[s,i]\le dist[i,t]\),存在一点 \(j\) 使得 \(dist[i,j]>dist[i,t]\),则存在 \(dist[s,i]+dist[i,j]>dist[s,t]\),与树的直径矛盾。
根据求解树的直径的方法,将其两个端点 \(s,t\) 求出来,然后分别计算两个端点到其余各点的距离,答案就是 \(max(dist[i,s],dist[i,t])\) 的累加值将去重复计算的树的直径的长度,因为在求和时,遍历所有点会遍历树的直径的两个端点,计算两次树的直径。
C++ 代码
// NC52867
#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = N * 2;
typedef long long LL;
int n, pre[N];
int h[N], e[M], ne[M], w[M], idx;
LL dist[N], dist1[N];
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u) {
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j != pre[u]) {
pre[j] = u;
dist[j] = dist[u] + w[i];
dfs(j);
}
}
}
void dfs1(int u) {
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j != pre[u]) {
pre[j] = u;
dist1[j] = dist1[u] + w[i];
dfs1(j);
}
}
}
int main() {
while (~scanf("%d", &n)) {
memset(h, -1, sizeof h);
idx = 0;
for (int i = 1; i < n; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
add(b, a, c);
}
memset(dist, 0, sizeof dist);
memset(pre, 0, sizeof pre);
pre[1] = -1;
dfs(1);
int k = 0;
for (int i = 1; i <= n; i ++)
if (dist[i] > dist[k])
k = i;
memset(dist, 0, sizeof dist);
memset(pre, 0, sizeof pre);
pre[k] = -1;
dfs(k);
LL len = 0;
for (int i = 1; i <= n; i ++)
if (dist[i] > dist[k]) {
len = dist[i];
k = i;
}
memset(dist1, 0, sizeof dist1);
memset(pre, 0, sizeof pre);
pre[k] = -1;
dfs1(k);
LL res = 0;
for (int i = 1; i <= n; i++)
res += max(dist[i], dist1[i]);
res -= len;
printf("%lld\n", res);
}
return 0;
}
1012 NC26257 小雨坐地铁
题目描述
小雨所在的城市一共有 \(m\) 条地铁线,分别标号为 \(1\) 号线,\(2\) 号线,\(……\),\(m\) 号线。整个城市一共有 \(n\) 个车站,编号为 \(1-n\)。其中坐 \(i\) 号线需要花费 \(a_i\) 的价格,每坐一站就需要多花费 \(b_i\) 的价格。\(i\) 号线有 \(c_i\) 个车站,而且这 \(c_i\) 个车站都已知,如果某一站有多条地铁线经过,则可以在这一站换乘到另一条地铁线,并且能多次换乘。现在小雨想从第 \(s\) 个车站坐地铁到第 \(t\) 个车站,地铁等待时间忽略不计,求最少花费的价格,若不能到达输出 \(-1\)。(地铁是双向的,所以 \(s\) 可能大于 \(t\))
输入描述
第一行输入四个正整数 \(n,m,s,t\),分别表示车站个数,地铁线数,起点站和终点站。
第二行到第 \(m+1\) 行,每行前三个数为 \(a_i,b_i,c_i\),分别表示坐 \(i\) 号线的价格,\(i\) 号线每坐一站多花的价格,\(i\) 号线车站个数。接下来 \(c_i\) 个数,表示 \(i\) 号线的每一个车站的编号,单调递增。
输出描述
共一行,一个数表示最小花费,若不能到达输出 \(-1\)。
示例
输入
5 2 1 4
2 2 3 1 3 5
2 1 4 2 3 4 5
输出
7
说明
坐 \(1\) 号线:花费 \(2\);
\(1→3\):花费 \(2\);
换乘 \(2\) 号线:花费 \(2\);
\(3→4\):花费 \(1\);
所以最小总花费为 \(7\)。
备注
\(1≤n≤10^3,1≤m≤500,1≤s,t≤n\)
\(1\le a_i,b_i\le 100,1\le c_i\le 100,\sum_{i=1}^m c_i\le 10^5\)
解题思路
每条地铁线都是一个图,那么这里有 \(m\) 个图,则将其视作多源最短路问题。引入虚拟源点和终点,虚拟源点到每条地铁线的权值为乘坐该地铁线的值,反之为零,相当于从虚拟源点进行上下车。建图完成之后进行 \(Dijkstra\) 求最短路即可。
具体的可以见代码。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 600010, M = 1000010;
typedef long long LL;
int n, m, s, t;
int h[N], e[M], ne[M], idx;
LL w[M], dist[N];
int tmp[N], st[N];
void add(int a, int b, LL c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void Dijkstra(int s) {
dist[s] = 0;
priority_queue<pair<LL, int>, vector<pair<LL, int>>, greater<pair<LL, int>>> q;
q.push({dist[s], s});
while (q.size()) {
auto t = q.top();
q.pop();
int ver = t.second;
if (st[ver])
continue;
st[ver] = true;
for (int i = h[ver]; ~i; i = ne[i]) {
int j = e[i];
if (dist[j] > dist[ver] + w[i]) {
dist[j] = dist[ver] + w[i];
q.push({dist[j], j});
}
}
}
}
int main() {
memset(h, -1, sizeof h);
memset(dist, 0x3f, sizeof dist);
scanf("%d%d%d%d", &n, &m, &s, &t);
for (int i = 0; i < m; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
for (int j = 0; j < c; j++) {
scanf("%d", &tmp[j]);
if (j > 0) {
add(i * n + tmp[j], i * n + tmp[j - 1], b);
add(i * n + tmp[j - 1], i * n + tmp[j], b);
}
add(i * n + tmp[j], m * n + tmp[j], 0);
add(m * n + tmp[j], i * n + tmp[j], a);
}
}
Dijkstra(m * n + s);
if (dist[m * n + t] == 0x3f3f3f3f3f3f3f3f)
dist[m * n + t] = -1;
printf("%lld\n", dist[m * n + t]);
return 0;
}
本文来自博客园,作者:Cocoicobird,转载请注明原文链接:https://www.cnblogs.com/Cocoicobird/p/19696956
浙公网安备 33010602011771号