热浪:
题目大致意思:
给你n个点m条边,给你一个起始的位置,一个终点的位置,问你这两个点的最短距离
朴素的Dijkstra算法:
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2510;
int n, c, ts, te;
string s;
int dis[N];//距离起点的最短距离
int g[N][N];
bool st[N];//这个点的距离是否已经被确定了
void dijkstra() {
for(int i = 1; i <= n; ++ i ) {//n次迭代
int t = -1;
for(int j = 1; j <= n; ++ j ) {
if((t == -1 || dis[j] < dis[t]) && !st[j]) {
t = j;
}
}
st[t] = true;
for(int j = 1; j <= n; ++ j ) {
dis[j] = min(dis[j], dis[t] + g[t][j]);
}
}
}
signed main() {
cin >> n >> c >> ts >> te;
memset(g, 0x3f, sizeof g);
memset(dis, 0x3f, sizeof dis);
dis[ts] = 0;
for(int i = 1; i <= c; ++ i ) {
int x, y, v;
cin >> x >> y >> v;
g[x][y] = g[y][x] = min(g[x][y], v);//双向的
}
dijkstra();
cout << dis[te] << endl;
return 0;
}
运行的时间:239ms
spfa算法:
注意: 题目说的是无向图,因此边数要乘以2,否则会TLE
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2510, M = 6200 * 2 + 10;//边数要开2倍 因为是无向图
int h[N], e[M], ne[M], v[M], idx;
int t, c, ts, te;
queue<pair<int, int>>q;
int dis[N];
bool st[N];
void add(int x, int y, int va) {
e[idx] = y;
ne[idx] = h[x];
v[idx] = va;
h[x] = idx++;
}
void spfa() {
dis[ts] = 0;//起点的距离置为0
q.push({0, ts});
st[ts] = true;
while(q.size()) {
auto p = q.front();
int d = p.second, va = p.first;
q.pop();
st[d] = false;//出去了
for(int i = h[d]; i != -1; i = ne[i]) {
int j = e[i];
if(dis[j] > dis[d] + v[i]) {//这里写的是v[i]表示的是这两个点之间的距离
dis[j] = dis[d] + v[i];
if(!st[j]) {
q.push({dis[j], j});
st[j] = true;
}
}
}
}
}
signed main() {
cin >> t >> c >> ts >> te;
memset(h, -1, sizeof h);
memset(dis, 0x3f, sizeof dis);
for(int i = 1; i <= c; ++ i ) {
int x, y, va;
cin >> x >> y >> va;
add(x, y, va);
add(y, x, va);//无向图
}
spfa();
cout << dis[te] << endl;
return 0;
}
运行的时间:31ms
因此可以得出:
一般情况下spfa是比Dijkstra要快的,快的马上要10倍了,但是有的时候,spfa会被卡,要是被卡的话,时间复杂度会达到O(nm)
香甜的黄油:
题目大致意思:
给你一个连通的图, 让你随便选择起点(但是最后的答案要小), 让你求其他点到这个起点的和的最小值
分析:
去枚举每个牧场让他们每个都作为起点,然后求出来最小的答案,用朴素版的Dijkstra是会卡的, 但是优化一下,也就是优化版的Dijkstra就可以过了,或者可以用几乎万能的spfa()
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 810, M = 1450 * 2 + 10;//注意是无向边
int n, p, c;
int id[N], h[N], e[M], w[M], ne[M], idx;
bool st[N];
int dis[N];
void add(int x, int y, int v) {
e[idx] = y;
w[idx] = v;
ne[idx] = h[x];
h[x] = idx++;
}
int spfa(int x) {
queue<pair<int, int>>q;
dis[x] = 0;//作为起点
q.push({0, x});
st[x] = true;
while(q.size()) {
auto t = q.front();
q.pop();
int dian = t.second;
st[dian] = false;//出去了
for(int i = h[dian]; i != -1; i = ne[i]) {
int j = e[i];
if(dis[j] > dis[dian] + w[i]) {
dis[j] = dis[dian] + w[i];
if(!st[j]) {
q.push({dis[j], j});
st[j] = true;
}
}
}
}
int ret = 0;
for(int i = 1; i <= n; ++ i ) {
int j = id[i];
if(dis[j] == 0x3f3f3f3f)return 0x3f3f3f3f;
ret += dis[j];
}
return ret;
}
signed main() {
cin >> n >> p >> c;//奶牛数 牧场数 道路数
for(int i = 1; i <= n; ++ i ) {
cin >> id[i];//记录的是牛棚(有牛在那)
}
memset(h, -1, sizeof h);
for(int i = 1; i <= c; ++ i ) {//牛棚和牛棚之间的距离
int x, y, z;
cin >> x >> y >> z;
add(x, y, z);
add(y, x, z);
}
int ret = 0x3f3f3f3f;
for(int i = 1; i <= p; ++ i ) {//这里枚举的是牧场的数量
memset(dis, 0x3f, sizeof dis);
memset(st, 0, sizeof st);
ret = min(ret, spfa(i));//枚举每个i作为起点
}
cout << ret << endl;
return 0;
}
运行时间:526ms
最小花费
题目大致意思:
给你n个人,给你m个关系 表示这两个人之间转账需要的手续率
问你最少需要多少钱使得转账后B可以收到100元
分析:
之前做的都是边的问题,让你求最短路,现在是同样的板子,只不过换了个问法
逆向思维,问题转换为,给B 100元,每次转钱都给你手续费,问你转到A的时候的钱是多少
因此也就是把之前的dis换为了ret数组(表示的是由B转过来的钱数)
for(int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if(ret[j] > ret[t] / (1.0 - w[i] * 0.01) ) {
ret[j] = ret[t] / (1.0 - w[i] * 0.01);
if(!st[j]) {
q.push(j);
st[j] = true;//进去了
}
}
}
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2010, M = 2e5 + 10;
int h[N], e[M], ne[M], w[M], idx;
int n, m;
bool st[N];
double ret[N];
void add(int x, int y, int v) {
e[idx] = y;
w[idx] = v;
ne[idx] = h[x];
h[x] = idx++;
}
void spfa(int x) {
queue<int>q;
q.push(x);
st[x] = true;
ret[x] = 100.0;
while(q.size()) {
int t = q.front();
q.pop();
st[t] = false;//出去了
for(int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if(ret[j] > ret[t] / (1.0 - w[i] * 0.01) ) {
ret[j] = ret[t] / (1.0 - w[i] * 0.01);
if(!st[j]) {
q.push(j);
st[j] = true;//进去了
}
}
}
}
}
signed main() {
cin >> n >> m;
memset(h, -1, sizeof h);
for(int i = 1; i <= n; ++ i ) {
ret[i] = 1e9;
}
for(int i = 1; i <= m; ++ i ) {
int x, y, v;
cin >> x >> y >> v;
add(x, y, v);
add(y, x, v);//无向图
}
int start, end;
cin >> start >> end;
spfa(start);
printf("%.8lf", ret[end]);
return 0;
}
运行时间:235 ms
最优乘车 :
题目大致意思:
给你n个公交车的路线,让你求从1到n最少换乘车的次数
分析:
转换思路, 用dis[i]表示的是1到i需要几步能到达的点,要是在同一班车的话,我们就认为是1步,答案就是dis[n] - 1
但是这里他没给你每一班车的数量 我们就用到了 stringstream (字符串流)这个字符串容器,也可以认为是一串分割后的字符串数组
- 可以自动分割字符串,把字符串中的 空格省略
- 可以在移出字符串流后会 自动类型转换
stringstream (字符串流)的代码演示:
#include<bits/stdc++.h>
using namespace std;
string s;
signed main() {
s = "code change the word";
stringstream ssin(s);//让这个字符串 进到名字叫ssin的符串容器里面
string t;//暂时用来接收的
//我在把每个字符给吐出来 吐出来给谁的 你让我给是字符串p 我就自动给你转换为字符串的类型 还除去空了啦
while(ssin >> t)cout << t << endl;
return 0;
}
stringstream (字符串流)运行结果:
code
change
the
word
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 510;
int m, n ;
bool g[N][N];
int dis[N];// dis[i] 表示的是1到i需要几步能到达的点 要是再同一班车能经过的话 我们就认为1步就可以
int stop[N];
void bfs() {//和spfa一个样子 spfa还是牛
queue<int>q;
memset(dis, 0x3f, sizeof dis);
q.push(1);
dis[1] = 0;//起点是1号点
while(q.size()) {
int t = q.front();
q.pop();
for(int i = 1; i <= n; ++ i ) {
if(g[t][i] && dis[i] > dis[t] + 1) {
dis[i] = dis[t] + 1;//第一次搜到的就是最短的
q.push(i);
}
}
}
}
signed main() {
cin >> m >> n;
//建图是最难的 这m个 怎么能去读入数据呢
string line;
getline(cin, line);//吸收掉回车
while(m--) {
getline(cin, line);//因为你不知道他一行会给你多少个 这个遇到回车会停止读入的
stringstream ssin(line);
int cnt = 0, p ;
while(ssin >> p)stop[++cnt] = p;
//每个汽车的前站达到后站的距离都为1 因为是同一辆车 未换车
for(int j = 1; j <= cnt; ++ j ) {
for(int k = j + 1; k <= cnt; ++ k ) {
g[stop[j]][stop[k]] = true;
}
}
}
bfs();
if(dis[n] > 0x3f3f3f3f / 2) {
cout << "NO" << endl;
return 0;
}
cout << max(dis[n] - 1, 0) << endl;//换乘的次数 就是距离 - 1
return 0;
}
/*
这里用到了 stringstream (字符串流)这个字符串容器,也可以认为是一串分割后的字符串数组
它的作用:
可以自动分割字符串,把字符串中的 空格省略
可以在移出字符串流后会 自动类型转换
建图 每个车站到达后面的车站的距离都是1
那么1到达n的最短距离就是转车次数+1
*/