Acwing 1135 新年好
重庆城里有 n 个车站,m 条 双向 公路连接其中的某些车站。
每两个车站最多用一条公路连接,从任何一个车站出发都可以经过一条或者多条公路到达其他车站,但不同的路径需要花费的时间可能不同。
在一条路径上花费的时间等于路径上所有公路需要的时间之和。
佳佳的家在车站 1,他有五个亲戚,分别住在车站 a,b,c,d,e
过年了,他需要从自己的家出发,拜访每个亲戚(顺序任意),给他们送去节日的祝福。
怎样走,才需要最少的时间?
输入格式
第一行:包含两个整数 n,m 分别表示车站数目和公路数目。
第二行:包含五个整数 a,b,c,d,e 分别表示五个亲戚所在车站编号。
以下 m 行,每行三个整数 x,y,t 表示公路连接的两个车站编号和时间。
输出格式
输出仅一行,包含一个整数 T,表示最少的总时间。
数据范围
1
≤
n
≤
50000
1≤n≤50000
1≤n≤50000
1
≤
m
≤
1
0
5
1≤m≤10^5
1≤m≤105
1
<
a
,
b
,
c
,
d
,
e
≤
n
1<a,b,c,d,e≤n
1<a,b,c,d,e≤n
1
≤
x
,
y
≤
n
1
≤
x
,
y
≤
n
1≤x,y≤n1≤x,y≤n
1≤x,y≤n1≤x,y≤n
1
≤
t
≤
100
1≤t≤100
1≤t≤100
思路
本题会卡掉spfa 所以用堆优化的Dijkstra做
先预处理 每个点到其他点的最短距离
再 d f s dfs dfs暴力枚举访问的顺序 取最小值
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;
const int N = 500010, INF = 0x3f3f3f3f;
int n, m;
int dist[6][N];
int source[6];
bool st[N];
vector<pair<int, int>>vec[N];
void dijkstra(int start, int dist[]) {
memset(dist, 0x3f, N * 4);
dist[start] = 0;
memset(st, false, sizeof st);
priority_queue<PII, vector<PII>, greater<PII>>heap;
heap.push({ 0,start });
while (!heap.empty()) {
auto t = heap.top();
heap.pop();
int ver = t.second;
if (st[ver])continue;
st[ver] = true;
for (int i = 0; i < vec[ver].size(); ++i) {
int j = vec[ver][i].second;
int w = vec[ver][i].first;
if (dist[j] > dist[ver] + w) {
dist[j] = dist[ver] + w;
heap.push({ dist[j],j });
}
}
}
}
int dfs(int u, int start, int distance) {
if (u == 6)return distance;
int res = 0x3f3f3f3f;
for (int i = 1; i <= 5; ++i) {
int next_ = source[i]; //下一个点的编号
if (!st[i]) {
st[i] = true;
res = min(res, dfs(u + 1, i, distance + dist[start][next_]));
st[i] = false;
}
}
return res;
}
int main() {
cin >> n >> m;
source[0] = 1;
for (int i = 1; i <= 5; ++i)scanf("%d", &source[i]);
while (m--) {
int u, v, w; scanf("%d%d%d", &u, &v, &w);
vec[u].push_back({ w,v });
vec[v].push_back({ w,u });
}
for (int i = 0; i < 6; ++i) {
dijkstra(source[i], dist[i]);
}
memset(st, false, sizeof st);
printf("%d\n", dfs(1, 0, 0));
return 0;
}
AcWing 340. 通信线路
在郊区有 N 座通信基站,P 条 双向 电缆,第 i 条电缆连接基站 A i A_i Ai和 B i B_i Bi。
特别地,1 号基站是通信公司的总站,N 号基站位于一座农场中。
现在,农场主希望对通信线路进行升级,其中升级第 i 条电缆需要花费 L i L_i Li。
电话公司正在举行优惠活动。
农产主可以指定一条从 1 号基站到 N 号基站的路径,并指定路径上不超过 K 条电缆,由电话公司免费提供升级服务。
农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。
求至少用多少钱可以完成升级。
输入格式
第1行:三个整数N,P,K。
第2…P+1行:第 i+1 行包含三个整数 A i , B i , L i A_i,B_i,L_i Ai,Bi,Li
输出格式
包含一个整数表示最少花费。
若1号基站与N号基站之间不存在路径,则输出”-1”。
数据范围
0
≤
K
<
N
≤
1000
0≤K<N≤1000
0≤K<N≤1000
1
≤
P
≤
10000
1≤P≤10000
1≤P≤10000
1
≤
L
i
≤
1000000
1≤Li≤1000000
1≤Li≤1000000
思路
题意为求一条从1-n的路径上的第k+1大的边的权值的最小值
因为是求最大值的最小值 可以想到用二分做
二分第k+1大的边的权值 然后把 大于这条边的边权值设置为0 小于这条边的边权值设置为1
整个图中只有权值为0/1的边 所以可以用双端队列广搜做
当 d i s t [ n ] > k dist[n] > k dist[n]>k时 说明mid的值偏小
当 d i s t [ n ] < = k dist[n] <= k dist[n]<=k时 说明mid的值可能为答案或者偏大
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;
const int N = 1010;
int n, m, k;
vector<PII>vec[N];
deque<int>q;
bool st[N];
int dist[N];
bool check(int bound) {
memset(st, false, sizeof st);
memset(dist, 0x3f, sizeof dist);
q.push_back(1);
dist[1] = 0;
while (!q.empty()) {
auto t = q.front();
q.pop_front();
if (st[t])continue;
st[t] = true;
for(int i = 0 ;i < vec[t].size();++i){
int j = vec[t][i].second;
int v = vec[t][i].first > bound;
if (dist[j] > dist[t] + v) {
dist[j] = dist[t] + v;
if (!v) q.push_front(j);
else q.push_back(j);
}
}
}
return dist[n] <= k;
}
int main() {
cin >> n >> m >> k;
while (m--) {
int u, v, w; cin >> u >> v >> w;
vec[u].push_back({ w,v });
vec[v].push_back({ w,u });
}
int l = 0, r = 1e6 + 1;
while (l < r) {
int mid = l + r >> 1;
if (check(mid))r = mid;
else l = mid + 1;
}
if (r == 1e6 + 1)r = -1;
cout << r << endl;
return 0;
}
AcWing 342. 道路与航线
农夫约翰正在一个新的销售区域对他的牛奶销售方案进行调查。
他想把牛奶送到T个城镇,编号为1~T。
这些城镇之间通过R条道路 (编号为1到R) 和P条航线 (编号为1到P) 连接。
每条道路 i 或者航线 i 连接城镇 A i A_i Ai到 B i B_i Bi,花费为 C i C_i Ci。
对于道路, 0 ≤ C i ≤ 10 , 000 0≤C_i≤10,000 0≤Ci≤10,000;然而航线的花费很神奇,花费Ci可能是负数( − 10 , 000 ≤ C i ≤ 10 , 000 −10,000≤C_i≤10,000 −10,000≤Ci≤10,000)。
道路是双向的,可以从 A i A_i Ai到 B i B_i Bi,也可以从 B i B_i Bi到 A i A_i Ai,花费都是 C i C_i Ci。
然而航线与之不同,只可以从 A i A_i Ai到 B i B_i Bi。
事实上,由于最近*太嚣张,为了社会和谐,出台了一些政策:保证如果有一条航线可以从 A i A_i Ai到 B i B_i Bi,那么保证不可能通过一些道路和航线从 B i B_i Bi回到 A i A_i Ai。
由于约翰的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。
他想找到从发送中心城镇S把奶牛送到每个城镇的最便宜的方案。
输入格式
第一行包含四个整数T,R,P,S。
接下来R行,每行包含三个整数(表示一个道路) A i , B i , C i A_i,B_i,C_i Ai,Bi,Ci
接下来P行,每行包含三个整数(表示一条航线) A i , B i , C i A_i,B_i,C_i Ai,Bi,Ci
输出格式
第1…T行:第 i 行输出从 S 到达城镇 i 的最小花费,如果不存在,则输出“NO PATH”。
数据范围
1
≤
T
≤
25000
1≤T≤25000
1≤T≤25000
1
≤
R
,
P
≤
50000
1≤R,P≤50000
1≤R,P≤50000
1
≤
A
i
,
B
i
,
S
≤
T
1≤A_i,B_i,S≤T
1≤Ai,Bi,S≤T
思路
d i j k s t r a + t o p s o r t dijkstra + topsort dijkstra+topsort
没有负权边的图可以用 D i j k s t r a Dijkstra Dijkstra 有向无环图不管边权正负都可以用 S P F A SPFA SPFA
1.先输入所有双向道路 d f s dfs dfs出所有连通块 计算两个数组: i d [ ] id[] id[]存储每个点属于哪个连通块;$vectorblock[] $存储每个连通块里有哪些点;
2.输入所有航线,同时统计出每个连通块的入度
3.按照拓扑序依次处理每个连通块。先将所有入度为0的连通块的编号加入队列中
4.每次从队头取出一个连通块的编号 b i d bid bid
5.将该 b l o c k [ b i d ] block[bid] block[bid]中的所有点加入堆中,然后对堆中所有点跑 D i j k s t r a Dijkstra Dijkstra算法
6.每次取出堆中距离最小的点的 v e r ver ver
7.然后遍历 v e r ver ver的所有邻点 j j j。如果 i d [ v e r ] = = i d [ j ] id[ver] == id[j] id[ver]==id[j]那么如果j能被更新 将j插入堆中;如果 i d [ v e r ] ! = i d [ j ] id[ver] != id[j] id[ver]!=id[j]
则将 i d [ j ] id[j] id[j] 这个连通块的入度减1,如果减成0了,则将其插入拓扑排序的队列中
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;
const int N = 25010;
int n, mr, mp, S;
int id[N], dist[N], din[N];
vector<int>block[N];
vector<PII>vec[N];
bool st[N];
int bcnt;
queue<int>q;
void dijkstra(int bid) {
priority_queue<PII, vector<PII>, greater<PII>>heap;
for (int i = 0; i < block[bid].size(); ++i) {
int j = block[bid][i];
heap.push({ dist[j],j });
}
while (heap.size()) {
auto t = heap.top();
heap.pop();
int ver = t.second;
if (st[ver])continue;
st[ver] = true;
for (int i = 0; i < vec[ver].size(); ++i) {
int j = vec[ver][i].second;
int dis = vec[ver][i].first;
if (dist[j] > dist[ver] + dis) {
dist[j] = dist[ver] + dis;
if (id[j] == id[ver])heap.push({ dist[j],j });
}
if (id[j] != id[ver] && --din[id[j]] == 0)q.push(id[j]);
}
}
}
void topsort() {
memset(dist, 0x3f, sizeof dist);
dist[S] = 0;
for (int i = 1; i <= bcnt; ++i) {
if (!din[i])
q.push(i);
}
while (q.size()) {
int t = q.front();
q.pop();
dijkstra(t);
}
}
void dfs(int u, int bid) {
id[u] = bid;
block[bid].push_back(u);
for (int i = 0; i < vec[u].size(); ++i) {
int j = vec[u][i].second;
if (!id[j])
dfs(j, bid);
}
}
int main() {
cin >> n >> mr >> mp >> S;
while (mr--) {
int u, v, w;
cin >> u >> v >> w;
vec[u].push_back({ w,v });
vec[v].push_back({ w,u });
}
for (int i = 1; i <= n; ++i) {
if (!id[i])
dfs(i, ++bcnt);
}
while (mp--) {
int u, v, w;
cin >> u >> v >> w;
vec[u].push_back({ w,v });
din[id[v]]++;
}
topsort();
for (int i = 1; i <= n; ++i) {
if (dist[i] > INF / 2)puts("NO PATH");
else printf("%d\n", dist[i]);
}
return 0;
}
AcWing 341 最优贸易
C国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市。
任意两个城市之间最多只有一条道路直接相连。
这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为1条。
C国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。
但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。
商人阿龙来到C国旅游。
当他得知“同一种商品在不同城市的价格可能会不同”这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚一点旅费。
设C国 n 个城市的标号从 1~n,阿龙决定从1号城市出发,并最终在 n 号城市结束自己的旅行。
在旅游的过程中,任何城市可以被重复经过多次,但不要求经过所有 n 个城市。
阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品——水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。
因为阿龙主要是来C国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。
现在给出 n 个城市的水晶球价格,m 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。
请你告诉阿龙,他最多能赚取多少旅费。
注意:本题数据有加强。
输入格式
第一行包含 2 个正整数 n 和 m,中间用一个空格隔开,分别表示城市的数目和道路的数目。
第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这 n 个城市的商品价格。
接下来 m 行,每行有 3 个正整数,x,y,z,每两个整数之间用一个空格隔开。
如果z=1,表示这条道路是城市 x 到城市 y 之间的单向道路;如果z=2,表示这条道路为城市 x 和城市 y 之间的双向道路。
输出格式
一个整数,表示答案。
数据范围
1≤n≤100000
1≤m≤500000
1≤各城市水晶球价格≤100
思路
d m i n [ k ] dmin[k] dmin[k] 表示 1 − k 1-k 1−k 价格的最小值 d m a x [ k ] dmax[k] dmax[k] 表示 k − n k-n k−n 价格的最大值
S P F A SPFA SPFA求出 d m i n dmin dmin和 d m a x dmax dmax后 遍历 1 − n 1-n 1−n 求出 d m a x [ k ] − d m i n [ k ] dmax[k] - dmin[k] dmax[k]−dmin[k] 的最大值
在求 d m i n dmin dmin和 d m a x dmax dmax时 需要建反图
用 D i j k s t r a Dijkstra Dijkstra会超时 所以用 S P F A SPFA SPFA
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m;
int w[N];
int dmin[N], dmax[N];
vector<int>vec1[N];
vector<int>vec2[N];
queue<int>q;
bool st[N];
void spfa(vector<int>vec[], int dist[], int type) {
if (type == 0) {
memset(dist, 0x3f, sizeof dmin);
dist[1] = w[1];
q.push(1);
}
else {
memset(dist, 0, sizeof dmax);
dist[n] = w[n];
q.push(n);
}
while (q.size()) {
int t = q.front();
q.pop();
st[t] = false;
for (int i = 0; i < vec[t].size(); ++i) {
int j = vec[t][i];
if ((type == 0 && dist[j] > min(dist[t], w[j])) || (type == 1 && dist[j] < max(dist[t], w[j]))) {
if (type == 0) dist[j] = min(dist[t], w[j]);
else dist[j] = max(dist[t], w[j]);
if (!st[j]) {
q.push(j);
st[j] = true;
}
}
}
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)scanf("%d", &w[i]);
while (m--) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
vec1[u].push_back(v);
vec2[v].push_back(u);
if (w == 2) {
vec1[v].push_back(u);
vec2[u].push_back(v);
}
}
spfa(vec1, dmin, 0);
spfa(vec2, dmax, 1);
int res = -1;
for (int i = 1; i <= n; ++i) {
res = max(res, dmax[i] - dmin[i]);
}
cout << res << endl;
return 0;
}