目录
1)热浪(板子题)
题意概述
输入样例 :
第一行 :给定 T个点 C条边 起点Ts 终点Te
剩下的C行是对 某一条边的描述 :从a-->>b 有 一条权重为 c 的边
根据数据范围推出用哪种最短路:
本题中 点数的最大值为 2500,边数的范围是 6200
下面是三种的最短路的算法 的实现方式
(朴素dijkstra) O(n2)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2510, M = 6210;
int n, m, s, t;
int g[N][N];
int dist[N];
bool st[N];
void dijkstra()
{
memset(dist, 0x3f, sizeof dist);
memset(st, 0, sizeof st);
dist[s] = 0;
for (int i = 1; i <= n; i ++ )
{
int t = -1;
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
st[t] = true;
for (int j = 1; j <= n; j ++ )
dist[j] = min(dist[j], dist[t] + g[t][j]);
}
}
int main()
{
memset(g, 0x3f, sizeof g);
cin >> n >> m >> s >> t;
while (m -- )
{
int a, b, c;
cin >> a >> b >> c;
g[a][b] = g[b][a] = min(g[a][b], c);
}
dijkstra();
cout << dist[t] << endl;
return 0;
}
(堆优化dijkstra) O((n+m)logm)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 2510, M = 6210 << 1;
int n, m, s, t;
int h[N], e[M], ne[M], w[M], idx;
int dist[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
void dijkstra()
{
memset(dist, 0x3f, sizeof dist);
memset(st, 0, sizeof st);
priority_queue<PII, vector<PII>, greater<>> heap;
heap.push({0, s});
dist[s] = 0;
while (!heap.empty())
{
PII t = heap.top();
heap.pop();
st[t.y] = true;
for (int i = h[t.y]; ~i; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[t.y] + w[i])
{
dist[j] = dist[t.y] + w[i];
heap.push({dist[j], j});
}
}
}
}
int main()
{
memset(h, -1, sizeof h);
cin >> n >> m >> s >> t;
while (m -- )
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
dijkstra();
cout << dist[t] << endl;
return 0;
}
(spfa) O(m)
平均O(m),最坏O(nm)
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=3000,M=20000;
int h[N],w[M],e[M],ne[M],idx;
bool ste[N];
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
int spfa(int st,int ed)
{
int dist[M];
memset(dist,0x3f,sizeof dist);
queue<int> q;
q.push(st);
dist[st]=0;
ste[st]=true;
while(q.size())
{
int t=q.front();
q.pop();
ste[t]=false;
for(int i=h[t];~i;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
if(!ste[j])
{
ste[j]=true;
q.push(j);
}
}
}
}
return dist[ed];
}
int main()
{
int n,m,st,ed;
cin>>n>>m>>st>>ed;
memset(h,-1,sizeof h);
for(int i=0;i<m;i++)
{
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
add(a,b,w),add(b,a,w);
}
int t=spfa(st,ed);
cout<< t <<endl;
return 0;
}
三种代码运行时间对比:
本题中 点数最大值为 2500,边数的最大值是 6200
由上到下是:spfa(m) 堆优化dij(mlogn) 朴素版dij(n^2)
2)信使
题意概述:
样例解释:
第一行输入 点数n 边数m
接下来的m行:
输入 a->b 的权重为 c
思路解析:
这道题核心的地方就是知道了 起点(1号点)
但是不知道终点(不一定是N号点距离起点的距离最远)
那么题目中最关键的一句话是:直至所有岗哨都接到命令后,送信才算成功
(1)对于有岗哨不能接到命令的情况:处理完最短路后,仍然有dist[岗哨]==0x3f3f3f3f
(2)因为每一个岗哨都有充足的哨兵:可以类比为:
初始的1个人,每经历过一个岗哨,那么就会影分身向附近的没有走过的岗哨走去,那么再把每一个分身都当成初始的那一个人,就有经历了最大dist[岗哨],一定可以把所有岗哨走完
(仔细体会)
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110;
int g[N][N];
int dist[N];
int n,m;
bool st[N];
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=1;i<=n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
{
if(!st[j] && (t==-1 || dist[t]>dist[j]))
{
t=j;
}
}
st[t]=true;
for(int k=1;k<=n;k++)
{
dist[k]=min(dist[k],dist[t]+g[t][k]);
}
}
//解释中的情况
int res=-1;
for(int i=1;i<=n;i++)
{
if(dist[i]==0x3f3f3f3f) return -1;
res=max(res,dist[i]);
}
return res;
}
int main()
{
cin>>n>>m;
memset(g,0x3f,sizeof g);
for(int i=1;i<=m;i++)
{
int a,b,c;
cin>>a>>b>>c;
if(a==b) continue;
g[a][b]=g[b][a]=min(g[a][b],c);
}
cout<<dijkstra()<<endl;
return 0;
}
3)香甜的黄油
核心思路:
(1)理解题意
<1>要求的是所有 奶牛到 指定 牧场的最小距离之和--->>其实已经有"多源最短路那味了"
(2)核心
<1>最短路枚举所有 牧场,然后求所有 奶牛到该牧场的最小距离,将所有奶牛到牧场的距离都加到 一个数组res[该牧场]中
·<2>然后枚举所有牧场,用ans记录 最小的res[牧场]
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 810, M = 3000, INF = 0x3f3f3f3f;
int n, p, m;
int id[N];
int h[N], e[M], w[M], ne[M], idx;
int dist[N], q[N], res[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
void spfa(int start)
{
memset(dist, 0x3f, sizeof dist);
dist[start] = 0;
queue<int> q;
//int hh = 0, tt = 1;
q.push(start);
st[start] = true;
while (q.size())
{
int t = q.front();
q.pop();
//if (hh == N) hh = 0;
st[t] = false;
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
if (!st[j])
{
//q[tt ++ ] = j;
q.push(j);
//if (tt == N) tt = 0;
st[j] = true;
}
}
}
}
for (int i = 1; i <= p; i ++)
if (dist[i] == INF) res[i] = INF;
else res[i] += dist[i];
}
int main()
{
cin >> n >> p >> m;
for (int i = 0; i < n; i ++ ) cin >> id[i];
memset(h, -1, sizeof h);
for (int i = 0; i < m; i ++ )
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
for (int i = 0; i < n; i ++ )
spfa(id[i]);
int ans = INF;
for (int i = 1; i <= p; i ++)
ans = min(ans, res[i]);
cout << ans << endl;
return 0;
}
4)最小花费
核心思路:
其实也就是把权重这个值 换成了 稍微数学一点而已--->>数学的东西还是手写舒服
注意:这里dist相当于累计的这一串 (100-z1)(100-z2)……
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N = 2010 , M = 200010;
int e[M] , ne[M] , h[N] , idx;
double d[M] , w[M];
bool st[N];
int n , m;
void add(int a , int b , double c)
{
e[idx] = b , ne[idx] = h[a] , w[idx] = c , h[a] = idx++;
}
double spfa(int a , int b)
{
queue<int> q;
q.push(a);
d[a] = 1;
while(q.size())
{
int t = q.front();
q.pop();
st[t] = false;
for(int i = h[t] ; ~i ; i = ne[i])
{
int j = e[i];
if(d[j] < d[t] * (1 - w[i]))//此时的 w[i]是 扣除的 比率
{ //那么实际上所转的 钱是 d[]*(1-扣除的比率) == 转后的钱(看题解二)
d[j] = d[t] * (1 - w[i]);
if(!st[j])
{
st[j] = true;
q.push(j);
}
}
}
}
return d[b];
}
int main()
{
cin >> n >> m;
memset(h , -1 , sizeof h);
while(m--)
{
int a , b , c;
cin >> a >> b >> c;
add(a , b , 1.0 * c / 100) , add(b , a , 1.0 * c / 100);
}
int a , b;
cin >> a >> b;
printf("%.8lf\n" , 100 / spfa(a , b));
return 0;
}
5) 最优乘车
核心思路:
题意概述:
要求的换乘的是最小次数
建图
两层循环,将一个结点后面的的所有点全部建为1
(1)如果,起点和终点都恰好 在这条路线上 那么 换乘的次数就是0,因此 输出答案需要-1
(2)如果,起点和终点可换成换乘一次即可,那么就有 换乘的次数为 1,与上同理
至于为什么将 边权建为 1,是为了方便换乘时好计算,只需在输出的时候-1即可
注意:
由于不知道该路线一共有多少个 车站
所以要用 stringstream 来 读入
Floyd算法:
#include<iostream>
#include<cstring>
#include<sstream>
using namespace std;
const int N=510;
int n,m;
int s[N][N];
int main()
{
cin>>m>>n;
memset(s,0x3f,sizeof s);
getchar();//吃回车
while(m--)
{
string line;
getline(cin,line);
stringstream scin(line);
int cnt=0,t,stop[N];
while(scin>>t) stop[cnt++]=t;
for(int i=0;i<cnt;i++)
for(int j=i+1;j<cnt;j++)
s[stop[i]][stop[j]]=1;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
s[i][j]=min(s[i][j],s[i][k]+s[k][j]);
if(s[1][n]>=0x3f3f3f3f) cout<<"NO";
else cout<<s[1][n]-1<<endl;
return 0;
}
广度优先BFS
#include<iostream>
#include<cstring>
#include<algorithm>
#include<sstream>
#include<queue>
using namespace std;
const int N=510;
int m,n;
bool g[N][N];
int dist[N];
int stop[N];
int q[N];
void bfs()
{
memset(dist,0x3f,sizeof dist);
queue<int> q;
q.push(1);
dist[1]=0;
while(q.size())
{
int t=q.front();
q.pop();
for(int i=1;i<=n;i++)//扩张:n个车站
{ //如果该车站在该条 路线存在 且 换成次数取小的
if(g[t][i] && dist[i]>dist[t]+1)
{
dist[i]=dist[t]+1;
q.push(i);
}
}
}
}
int main()
{
cin>>m>>n;
string line;//吃掉回车
getline(cin,line);
while(m--)
{
getline(cin,line);
stringstream ssin(line);
int cnt=0,p;//因为不知道 输入的车站个数
while(ssin>>p) stop[cnt++]=p;
for(int j=0;j<cnt;j++)
{
for(int k=j+1;k<cnt;k++)//将它之后的车站标 true
{ //该路线存在的条件
g[stop[j]][stop[k]]=true;
}
}
}
bfs();
if(dist[n]==0x3f3f3f3f) puts("NO");
else cout<<max(dist[n]-1,0)<<endl;
return 0;
}
dijkstra
#include <iostream>
#include <sstream>
#include <memory.h>
#include <queue>
using namespace std;
const int N = 510;
int n, m;
int stop[N];
/*
3 7
6 7
4 7 3 6
2 1 3 5
输出样例:
2
*/
int g[N][N];
int dist[N];
bool st[N];
int dij()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < n - 1; i++) {
int t = -1;
for (int j = 1; j <= n; j++) {
if (!st[j] && (t == -1 || dist[t] > dist[j])) {
t = j;
}
}
for (int j = 1; j <= n; j++) {
dist[j] = min(dist[j], dist[t] + g[t][j]);
}
st[t] = true;
}
return dist[n];
}
int main()
{
cin >> m >> n;
memset(g, 0x3f, sizeof g);
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 i = 0; i < cnt; i++) {
for (int j = i + 1; j < cnt; j++) {
g[stop[i]][stop[j]] = 1;
}
}
}
int ret = dij();
if ( ret > 0x3f3f3f3f/2) cout << "NO" << endl;
else cout << ret-1 << endl;
return 0;
}
作者:itdef
链接:https://www.acwing.com/solution/content/15468/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
6)昂贵的聘礼
核心思路:
<1>构造一个虚拟点0,那么就是求0-->>1 的所花金币的最短路
<2>关于怎么建图,我推荐根据样例自己在纸上画
<3>扩展边的时候额外+一个判断等级差距的条件即可
<4>剩下的就是模拟了,这个在注释已经非常详细了~~~
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110;
const int INF=0x3f3f3f3f;
int n,m;
int w[N][N],level[N];
int dist[N];
bool st[N];
int dijkstra(int down,int up)
{
memset(dist,0x3f,sizeof dist);
memset(st,0,sizeof st);
dist[0]=0;
for(int i=0;i<=n;i++)
{
int t=-1;
for(int j=0;j<=n;j++)
{
if(!st[j] && (t==-1 || dist[t]>dist[j]))
t=j;
}
st[t]=true;
for(int j=1;j<=n;j++)
{
if(level[j]>=down && level[j]<=up)//必须要在等级范围内 才可以进行交换
{
dist[j]=min(dist[j],dist[t]+w[t][j]);
}
}
}
return dist[1];
}
int main()
{
cin>>m>>n;//等级差距限制 物品的总数
memset(w,0x3f,sizeof w);//初始化所有边权为 INF
for(int i=1;i<=n;i++) w[i][i]=0;//防止自环
for(int i=1;i<=n;i++)//n个物品的描述
{
int price,cnt;
cin>>price>>level[i]>>cnt;//直接换的物品的价格 主人的地位等级 替代品的总数
w[0][i]=min(price,w[0][i]);//直接换的物品价格 -->> id:0 的虚拟点 -->> i的所需金币
while(cnt--)//替代品
{
int id,cost;
cin>>id>>cost;//替代品的编号 和 价格
w[id][i]=min(w[id][i],cost);//从 id -> i 所需 的金币数
}
}
int res=INF;
//从等级1的 下限 开始 枚举(因为1点的是终点)
for(int i=level[1]-m;i<=level[1];i++) res=min(res,dijkstra(i,i+m));
cout<<res<<endl;
return 0;
}
7)新年好
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define x first
#define y second
const int N=50010,M=2e5+10;
const int INF=0x3f3f3f3f;
typedef pair<int,int> PII;
int n,m;
int source[6];
bool st[N];
int dist[6][N];
int h[N],e[M],ne[M],w[M],idx;
void add(int a,int b,int c)
{
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
void dijkstra(int start,int dist[])
{
memset(dist,0x3f,4*N);//只是初始化 第 i 行 的数据(数组名做形参不能用 sizeof)
memset(st,0,sizeof st);
dist[start]=0;
priority_queue<PII,vector<PII>,greater<PII>> heap;
heap.push({0,start});//距离 点
while(heap.size())
{
auto t=heap.top();
heap.pop();
int ver=t.y;
int distance=t.x;
if(st[ver]) continue;
st[ver]=true;
for(int i=h[ver];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>distance+w[i])
{
dist[j]=distance+w[i];
heap.push({dist[j],j});
}
}
}
}
// 枚举每种拜访次序,求出最小距离
// 拜访了u个人,自己是第1个人;当前起点是source[start],当前走过的距离是distance
int dfs(int u,int start,int distance)
{
if(u==6) return distance;//u==6表示 拜访完5个亲戚,此时返回最短路
int res=INF;
for(int i=1;i<=5;i++)
{
if(!st[i])
{
int next=source[i];//走亲戚i
st[i]=true;
res=min(res,dfs(u+1,i,dist[start][next]+distance));
st[i]=false;
}
}
return res;
}
int main()
{
cin>>n>>m;//n个车站 m条路线
memset(h,-1,sizeof h);
source[0]=1;//佳佳家所在的车站编号
for(int i=1;i<=5;i++) cin>>source[i];//5个亲戚所在车站的编号
while(m--)
{
int x,y,t;
cin>>x>>y>>t;
add(x,y,t),add(y,x,t);//无向图
}
//6个点,分别求最短路
for(int i=0;i<6;i++) dijkstra(source[i],dist[i]);//只是初始化 第 i 行 的数据
/*
1. 共有6个人,起点是自己:第1个人
2. 当前是source[0]:佳佳
3. 当前走过的距离是0
*/
memset(st,0,sizeof st);
printf("%d\n",dfs(1,0,0));
return 0;
}
8)通信线路
思路解析:AcWing 340. 通信线路 - AcWing
#include <iostream>
#include <cstring>
#include <deque>
#include <algorithm>
using namespace std;
const int N = 1010 , M = 20010;
int e[M] , ne[M] , w[M] , h[N] , idx;
int dist[N];
int n , m , k;
bool st[N];
void add(int a , int b , int c)
{
e[idx] = b , ne[idx] = h[a] , w[idx] = c , h[a] = idx++;
}
bool check(int bound)
{
memset(dist , 0x3f , sizeof dist);
memset(st , 0 , sizeof st);
dist[1] = 0;
deque<int> q;
q.push_back(1);
while(q.size())
{
int t = q.front();
q.pop_front();
if(st[t]) continue;
st[t] = true;
for(int i = h[t] ; ~i ; i = ne[i])
{
int j = e[i] , x = w[i] > bound;
if(dist[j] > dist[t] + x)
{
dist[j] = dist[t] + x;
if(x) q.push_back(j);
else q.push_front(j);
}
}
}
return dist[n] <= k;
}
int main()
{
cin >> n >> m >> k;
memset(h , -1 , sizeof h);
while(m--)
{
int a , b , c;
cin >> a >> b >> c;
add(a , b , c) , add(b , a , c);
}
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;
}