能解决大多数的图 但是复杂度大,时间复杂度O(N^3)
对于Floyd而已一次预处理后可以知道任意两点的最短距离,值得注意的是要先将dist初始化,i==j情况下为0,即自己和自己的距离,千万不要inf,然后就是三次for遍历跑一次
初始化:
int k;cin >> n >> m >> k;for (int i = 1; i <= n; ++i)for (int j = 1; j <= n; ++j)dist[i][j] = i == j ? 0 : inf;
核心算法:
void Floyd() {for (int k = 1; k <= n; ++ k){for (int j = 1; j <= n; ++j){for (int g = 1; g <= n; ++g){dist[j][g] = min(dist[j][g], dist[j][k] + dist[k][g]);}}}
}
可以解决负环问题
Bellman-floyd算法基础模板题牛客
代码实现
#include<iostream>
#include<algorithm>
#include<string.h>
#include <set>
#include<cstdio>
#include <vector>
#include<map>
#include<string>
#include<cstring>
#include<numeric>
#include<math.h>
#include<queue>
#include <functional>
#include <bits/stdc++.h>
#include<unordered_map>
using namespace std;#define Fast std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define SQR(i) fixed<<setprecision(i)
#define int long long
typedef long long ll;
#define endl 'n'
#define int long long
#define inf 0x3f3f3f3f
#define inf_max 0x7f7f7f7fint n, m, s, t;
int dis[20000];struct node
{int v, w;
};
vector<node>arr[20000];void Bellman_Ford(int s) {for (int i = 1; i <= n; ++i) {dis[i] = inf;}dis[s] = 0;for(int k=1;k<n;++k)//迭代n-1次,每次更新一个for (int i = 1; i <= n; ++i)//对于第一个i找他匹配的所有边for (int j = 0; j < arr[i].size(); ++j)//对于每条边进行判断{int u = i, v = arr[i][j].v, w = arr[i][j].w;if (dis[v] > dis[u] + w)dis[v] = dis[u] + w;}//if i==n 则出现了重环必错
}void Solve() {cin >> n >> m >> s >> t;for (int i = 0; i < m; ++i){int u, v, w;cin >> u >> v >> w;arr[u].push_back({ v,w });arr[v].push_back({ u,w });}Bellman_Ford(s);if (dis[t] > inf / 2)cout << -1 << endl;else cout << dis[t] << endl;}signed main()
{std::ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);int t;Solve();
}
当然也可以这个样子存图,效果一样:
int n, m, s, t;
int dis[20000];struct node
{int u, v, w;
};
vector<node>arr;void Bellman_Ford(int s) {for (int i = 1; i <= n; ++i) {dis[i] = inf;}dis[s] = 0;for (int i = 1; i < n; ++i){for (int j = 0; j < 2 * m; ++j){int u = arr[j].u, v = arr[j].v, w = arr[j].w;if (dis[v] >dis[u] + w )dis[v] = dis[u] + w;}}}void Solve() {cin >> n >> m >> s >> t;for (int i = 0; i < m; ++i){int u, v, w;cin >> u >> v >> w;arr.push_back({ u,v,w });arr.push_back({ v,u,w });}Bellman_Ford(s);if (dis[t] > inf / 2)cout << -1 << endl;else cout << dis[t] << endl;}
//关于为什么要更新n-1次的问题!!!!
for k:n-1 这里的k是不超过k条边 如果k==n,一定存在负环!!!!!!
SPFA优化代码: 它也可以判断是否存在负环!!!!
int n, m, s, t;
int dis[20000];
bool vis[20000];
struct node
{int v, w;
};
vector<node>arr[20000];
int cnt[20000];void SPFA(int s) {for (int i = 0; i <= n; ++i)dis[i] = inf;dis[s] = 0; vis[s] = true;queue<int>q;q.push(s);while (!q.empty()){int t = q.front();q.pop();vis[t] = false;//使得队首可以入队for (int i = 0; i < arr[t].size(); ++i) {int j = arr[t][i].v;int w = arr[t][i].w;if (dis[j] > dis[t] + w) {dis[j] = dis[t] + w;cnt[j] = cnt[t] + 1;//判断负环//if (cnt[j] >= n) 等于n说明了负环!!!//{// cout << "找到了负环" << endl;//}if (!vis[j])q.push(j), vis[j] = true;}}}
}
Attention:
是不是存在负环是要把所有点压入queue,因为题目不是求从1点开始的负环,是所有点的负环!!! 如果改成从1开始的负环就不需要!!!
for (int i = 1; i <= n; ++i)q.push(i);
void Dij(int s)
{for (int i = 0; i <= n; ++i)dis[i] = inf, vis[i] = false;dis[s] = 0;for (int i = 1; i <= n; ++i) {//循环n次int t = -1;for (int j = 1; j <=n; ++j) {//找未标记中最小的dis的节点xif (!vis[i] && (t == -1 || dis[j] < dis[t]))t = j;if (t == -1)return;vis[t] = true;for (int j = 1; j <=n; ++j)//从想出发更新dis[j] = min(dis[j], dis[t] + arr[t][j]);}}
}
void Dijkstra(int s) {for (int i = 0; i <= n; ++i)dis[i] = inf, vis[i] = false;dis[s] = 0;priority_queue<PII, vector<PII>, greater<PII> >q;q.push({ 0,s });//0是dis, s是当然点while(!q.empty()){int t = q.top().second;//取出队首q.pop();if (vis[t])continue;//访问了就continuevis[t] = true;for (int i = 0, len = arr[t].size(); i < len; ++i) {//对于t这个点遍历更新和它匹配的点int v = arr[t][i].v, w = arr[t][i].w;if (dis[v] > dis[t] + w)//符合就更新后压入dis[v] = dis[t] + w, q.push({ dis[v],v });}}
}
void Dijkstra(int s) {for(int i=0; i<=2*n; ++i)dis[i]=inf,vis[i]= false;dis[s]=0;priority_queue<PII,vector<PII>,greater<PII> >q;//first is dis y is startwhile (!q.empty()) { //清空队列q.pop();}q.push({0,s});while(!q.empty()) {int tp().second;q.pop();if(vis[t])continue;vis[t]=true;for(int i=0,len=arr[t].size(); i<len; ++i) {int v=arr[t][i].v,w=arr[t][i].w;if(!vis[v]&&dis[v]>dis[t]+w)dis[v]=dis[t]+w,q.push({dis[v],v});}}
}
!!!!!题源this->
这是狄斯克算法,权值有向图;数据域小直接开数组:当然这是没有优化的算法,复杂度为O(n^2),我们可以采取优先队列进行最短路堆优化
#include<bits/stdc++.h>
using namespace std;
#define endl 'n'
#define inf 0x3f3f3f3f
typedef long long ll;
int a[505][505],dis[5000];//dis为到达节点的路,a为图
bool vis[5000];//标记集合
void Solve() {int n,m;cin>>n>>m;memset(a,inf,sizeof a);//inf 数组afor(int i=1;i<=m;++i){int x,y,w;cin>>x>>y>>w;a[x][y]=min(a[x][y],w);}// 存图for(int i=1;i<=n;++i)a[i][i]=0;memset(dis,inf,sizeof dis);//dis INFdis[1]=0;for(int cnt=1;cnt<=n;++cnt)//扩扑n次{int min_val=inf,x;for(int i=1;i<=n;++i)//更新最短路if(!vis[i]&&dis[i]< min_val)min_val=dis[i],x=i;vis[x]=true;//标记已经用过for(int y=1;y<=n;++y)//更新dis[y]=min(dis[y],dis[x]+a[x][y]);}if(dis[n]==inf)cout<<-1<<endl;else cout<<dis[n]<<endl;
}
signed main() {std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);Solve();}
//连接表
const int MAX_N=2e5+5,MAX_M=2e5+5;
int head[MAX_N],ver[MAX_M],edge[MAX_M],nxt[MAX_M],tot;
//插入一条x到y长度z的有向边
void add(int x,int y,int z)
{tot++;ver[tot]=y;edge[tot]=z;nxt[tot]=head[x];//在head[x]这条链开头插入新点head[x]=tot;
}
#include<bits/stdc++.h>
using namespace std;
#define endl 'n'
#define inf 0x3f3f3f3f
#define inf_max 0x7f7f7f7f
typedef long long ll;const int MAX_N=2e5+5,MAX_M=2e5+5;
int head[MAX_N],ver[MAX_M],edge[MAX_M],nxt[MAX_M],tot;
//插入一条x到y长度z的有向边
void add(int x,int y,int z)
{tot++;ver[tot]=y;edge[tot]=z;nxt[tot]=head[x];//在head[x]这条链开头插入新点head[x]=tot;
}
int n,m,dis[MAX_M];
bool vis[MAX_M];
//pair(dist[x],x)
struct com{bool operator()(pair<int,int>a,pair<int,int>b){return a.first<b.first;}
};
priority_queue<pair<int,int> > q;//大顶堆;
void Solve() {cin>>n>>m;for(int i=1;i<=m;++i){int x,y,z;cin>>x>>y>>z;add(x,y,z);}//存图memset(dis,inf_max,sizeof dis);dis[1]=0;q.push(make_pair(0,1));while(!q.empty()){int xp().second;q.pop();if(vis[x])continue;vis[x]=true;for(int i=head[x];i;i=nxt[i]){int y=ver[i],z=edge[i];if(dis[y]>dis[x]+z){dis[y]=dis[x]+z;q.push(make_pair(-dis[y],y));}}}if(dis[n]==inf_max)cout<<-1<<endl;else cout<<dis[n]<<endl;
}
signed main() {std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);Solve();}
路径统计问题-洛谷
有边数限制的最短路--ACwing
题解:
值得一提的是这题只可以Bellman算法,因为外层k循环是使得不超过k层边!!!
//防止串联,添加备份!!!!!
back干啥使的
back[j]表示每次进入第2重循环的dist数组的备份。
如果不加这个备份的话有可能会发生节点最短距离的串连;
比如说:
图1
现在从3号结点走到4号节点要想进行松弛操作就必须先得到dist[3],要想得到dist[3]就得知道dist[2];
dist[2]=2,现在dist[3]是正无穷,用2号结点来更新3号结点,dist[3]=2+3=5;现在要更新dist[4]了,此时dist[4]=正无穷
出现问题了,dist[4]是用dist[3]是用正无穷来更新还是5呢
用dist[3]=5来更新那是下一次i迭代的事情;
这道题说是不经过k条边,说不定下一次就到k条边了,所以还是得用正无穷来更新的
#include <bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define Fast std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
typedef pair<int, int > PII;
#define SQR(i) fixed<<setprecision(i)
#define int long long
typedef long long ll;
#define endl 'n'
const int N = 2e5 + 5;
#define int long long
#define iaa ((1ll<<31)<<31)-1
#define inf 12345678090int n,m,s;
bool vis[10500];
struct node{int v,w;
};
vector<node>arr[10500];
int dis[10500];int back[N];//备份数组放置串联
void Bellman_Ford(int s) {memset(dis,inf,sizeof dis);dis[1] = 0;for(int k=0;k<s;++k)//迭代n-1次,每次更新一个{memcpy(back, dis, sizeof dis);for (int i = 1; i <= n; ++i) {//对于第一个i找他匹配的所有边for (int j = 0; j < arr[i].size(); ++j)//对于每条边进行判断{int u = i, v = arr[i][j].v, w = arr[i][j].w;//if (dis[v] > dis[u] + w)dis[v] = dis[u] + w;dis[v]=min(dis[v],back[u]+w);}}}
}void Solve() {cin>>n>>m>>s;for(int i=0; i<m; ++i) {int u,v,w;cin>>u>>v>>w;arr[u].push_back({v,w});// arr[v].push_back({u,w});}Bellman_Ford(s);if(dis[n]>inf/2)cout<<"impossible"<<endl;else cout<<dis[n]<<endl;
}
signed main()
{Fast;Solve();return 0;
}
当然本题也可以不需要邻接表!!
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int dist[N],backup[N];
int k,n,m;
struct edge{int a;int b;int w;
}edge[N];
int bellman_ford()
{memset(dist,0x3f,sizeof dist);dist[1]=0;for(int i=1;i<=k;i++){memcpy(backup,dist,sizeof dist);for(int j=1;j<=m;j++){int a=edge[j].a,b=edge[j].b,w=edge[j].w;dist[b]=min(dist[b],backup[a]+w);}}if(dist[n]>=0x3f3f3f3f/2)return -1;else return dist[n];
}
int main()
{cin>>n>>m>>k;for(int i=1;i<=m;i++){int a,b,c;cin>>a>>b>>c;edge[i].a=a,edge[i].b=b,edge[i].w=c;}int t=bellman_ford();if(t==-1)puts("impossible");else cout<<t<<endl;
}
如果一个无向连通图不包含回路(连通图中不存在环),那么就是一个树。
int prime()
{//1memset(d,0x3f,sizeof d);d[1] = 0;//2int res = 0;//权重之和for(int i = 0;i<n;i++)//循环n次{//(1)不属于集合 && 距离集合最小的点int t = -1;for(int j = 1;j<=n;j++){if(!st[j] && (t == -1 || d[t] > d[j]))//t = j;}if(d[t] == INF) return INF;//(3)-把点t加到集合当中去,更新权值res += d[t];st[t] = true;//(2)for(int j = 1;j<=n;j++) d[j] = min(d[j],g[t][j]);}return res;
}
先将边权重按照从小到大排序,然后依次遍历对于不在同一集合的进行合并,ans是答案,使用了并查集知识,cnt<n-1表示无法得到最小生成树
Kruskal算法题目
int n, m;
int ans;//最小权重和
int p[N];
struct edge {int a, b, w;//重载<bool operator<(const edge& W)const{return w < W.w;//从小到大}
}edge[N];int find(int x)
{return p[x] == x ? x : p[x] = find(p[x]);
}
int Kruskal()
{int cnt = 0;sort(edge, edge + m);for (int i = 0; i <= n; ++i)p[i] = i;//初始化并查集for (int i = 0; i < m; ++i)//遍历{int a = edge[i].a, b = edge[i].b, w = edge[i].w;a = find(a); b = find(b);if (a != b)///a b不相等就合并{p[a] = b;ans += w;cnt++;}}return cnt;//cnt<n-1 说明了没用最小生成树}void Solve() {cin >> n >> m;for (int i = 0; i < m; ++i){int u, v, w;cin >> edge[i].a >> edge[i].b >> edge[i].w;}int t = Kruskal();if (t < n - 1)cout << "impossible" << endl;else cout << ans << endl;
}
本文发布于:2024-01-29 18:45:37,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170652513717501.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |