最短路:
SPFA:
特点:
代码短好写,负权边必备,可以判环,容易被卡成O(nm);
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 5e5 + 10;
const int inf = 2147483647;
int dist[MAXN];
int vis[MAXN];
vector<pair<int, int> >e[MAXN];
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, m, st;
cin >> n >> m >> st;
for (int i = 1; i <= n; i++) {
dist[i] = inf;
}
dist[st] = 0;
int u, v, w;
for (int i = 1; i <= m; i++) {
cin >> u >> v >> w;
e[u].push_back({v, w});
// cout<<i<<endl;
}
// for(int i=1;i<=n;i++){
// cout<<i<<':'<<endl;
// for(auto j:e[i]){
// cout<<j.first<<' '<<j.second<<endl;
// }
// }
queue<int> q;
q.push(st);
vis[st] = 1;
while (!q.empty()) {
st = q.front();
vis[st]=0;
q.pop();
for (auto i : e[st]) {
v = i.first;
w = i.second;
if (dist[st] + w < dist[v]) {
dist[v] = dist[st] + w;
if (!vis[v]) {
q.push(v);
vis[v] = 1;
}
}
}
}
for (int i = 1; i <= n; i++) {
cout << dist[i] << " \n"[i==n];
}
return 0;
}
差分约束
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int MAXN = 2e5 + 10;
const int inf = 0x3f3f3f3f;
int vis[MAXN];
int dist[MAXN];
int cnt[MAXN];
vector<pair<int, int> >e[MAXN];
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
dist[i] = inf;
}
int u, v, w;
for (int i = 1; i <= m; i++) {
cin >> v >> u >> w;
e[u].push_back({v, w});
}
for (int i = 1; i <= n; i++) {
e[0].push_back({i, 0});
}
queue<int>q;
q.push(0);
vis[0] = 1;
while (!q.empty()) {
u = q.front();
q.pop();
vis[u] = 0;
for (auto i : e[u]) {
v = i.first;
w = i.second;
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
if (!vis[v]) {
if (++cnt[v] > n) {
cout << "NO" << endl;
return 0;
}
vis[v] = 1;
q.push(v);
}
}
}
}
for (int i = 1; i <= n; i++) {
cout << dist[i] << " \n"[i == n];
}
return 0;
}
负环判断
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 2e3 + 10;
const int inf = 2147483647;
int dist[MAXN];
int vis[MAXN];
int cnt[MAXN];
vector<pair<int, int> >e[MAXN];
void sol() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
dist[i] = inf;
vis[i] = 0;
cnt[i] = 0;
e[i].clear();
}
int u, v, w;
for (int i = 1; i <= m; i++) {
cin >> u >> v >> w;
if (w >= 0) {
e[u].push_back({v, w});
e[v].push_back({u, w});
}
else {
e[u].push_back({v, w});
}
}
dist[1] = 0;
vis[1] = 1;
queue<int>q;
q.push(1);
int st;
int f = 0;
while (!q.empty()) {
st = q.front();
vis[st] = 0;
q.pop();
for (auto i : e[st]) {
v = i.first;
w = i.second;
if (dist[st] + w < dist[v]) {
dist[v] = dist[st] + w;
cnt[v] = cnt[st] + 1;
if(cnt[v] >= n){
f=1;
break;
}
if (!vis[v]) {
q.push(v);
vis[v] = 1;
}
}
}
if (f == 1)break;
}
if (f == 1)cout << "YES" << endl;
else cout << "NO" << endl;
}
signed main() {
int t;
cin >> t;
while (t--) {
sol();
}
}
dij
特点:
原本O(n^2),堆优化后O(n+m log),不能判负边
code:
堆优化版:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN=2e5+10;
const int inf=2147483647;
int dist[MAXN],vis[MAXN];
struct edge{
int dis,v;
bool operator < (const edge &x)const{
return x.dis<dis;
}
};//友元函数
priority_queue<edge>pq;
vector<pair<int,int> >e[MAXN];
#define f first
#define s second
signed main(){
int n,m,s;
cin>>n>>m>>s;
for(int i=1;i<=n;i++){
dist[i]=inf;
}
int u,v,w;
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
e[u].push_back({v,w});
}
dist[s]=0;
pq.push({0,s});
while(!pq.empty()){
auto j=pq.top();
pq.pop();
int u=j.v;
if(vis[u])continue;
vis[u]=1;
for(auto i:e[u]){
v=i.f,w=i.s;
if(dist[u]+w<dist[v]){
dist[v]=dist[u]+w;
if(!vis[v]){
pq.push({dist[v],v});
}
}
}
}
for(int i=1;i<=n;i++){
cout<<dist[i]<<" \n"[i==n];
}
}
最长路
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
const int inf=2147483647;
typedef long long ll;
ll dist[MAXN];
int vis[MAXN];
struct edge{
int v,w;
};
vector<edge>e[MAXN];
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n,m,s;
cin>>n>>m;
for(int i=1;i<=n;i++){
dist[i]=-1;
}
edge nd;
int u,v,w;
for(int i=1;i<=m;i++){
cin>>u>>nd.v>>nd.w;
e[u].push_back(nd);
}
dist[1]=0;
int cur=1;
while(!vis[cur]){
vis[cur]=1;
for(auto i:e[cur]){
v=i.v,w=i.w;
if(!vis[v] && (dist[cur]+w>dist[v] || dist[v]==-1)){
dist[v]=dist[cur]+w;
}
}
ll res=-1;
for(int i=1;i<=n;i++){
if(!vis[i] && dist[i]!=-1){
res=dist[i];
cur=i;
break;
}
}
}
cout<<dist[n]<<endl;
}
次短路
顾名思义,次短路就是第二短的路,但是也有许多不同之分。
- 严格/不严格:长度必须小于最短路/长度可以与最短路相同
- 能否走重复的路:(这个重复是与自身而言,也就是可不可以来回走同一条路)
根据以上两点,可以分成四类,不过就判断好条件跑堆优化的dij就行
非严格的,不可以走重复路的次短路
例题:集合位置
思路:
1.次短路和最短路一定不是同一条,所以先跑出来最短路,记录途中到达点
2.对于最短路上每一段路进行切割,跑不含这段路的最短路,取min
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN=205;
const int inf=1e18;
#define f first
#define s second
struct edge{
int v;
double dis;
bool operator < (const edge &x)const{
return x.dis<dis;
}
};
priority_queue<edge>pq;
pair<double,double>P[MAXN];//存点的坐标
vector<pair<int,double> >e[MAXN];
double dist[MAXN];
int pre[MAXN],vis[MAXN];
int n,m;
int str[MAXN],cnt=0;
double len(int x,int y){
return sqrt((P[x].s-P[y].s)*(P[x].s-P[y].s)+(P[x].f-P[y].f)*(P[x].f-P[y].f));
}
void dij(int k1,int k2){
if(k1==-1 && k2==-1){
int cur=1;
for(int i=1;i<=n;i++){
dist[i]=DBL_MAX;
}
dist[1]=0;
pq.push({1,0});
while(!pq.empty()){
auto i=pq.top();
pq.pop();
int u=i.v;
if(vis[u])continue;
vis[u]=1;
for(auto j:e[u]){
int v=j.f;
double w=j.s;
if(dist[u]+w<dist[v]){
pre[v]=u;
dist[v]=dist[u]+w;
if(!vis[v])pq.push({v,dist[v]});
}
}
}
cur=n;
while(cur!=0){
str[++cnt]=cur;
cur=pre[cur];
}
}
else{
for(int i=1;i<=n;i++){
vis[i]=0;
dist[i]=DBL_MAX;
}
dist[1]=0;
pq.push({1,0});
while(!pq.empty()){
auto i=pq.top();
pq.pop();
int u=i.v;
if(vis[u])continue;
vis[u]=1;
for(auto j:e[u]){
int v=j.f;
double w=j.s;
if(u==k1 && v==k2 || u==k2 && v==k1)continue;
if(dist[u]+w<dist[v]){
dist[v]=dist[u]+w;
if(!vis[v])pq.push({v,dist[v]});
}
}
}
}
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>P[i].f>>P[i].s;
}
int u,v;
for(int i=1;i<=m;i++){
cin>>u>>v;
double w=len(u,v);
e[u].push_back({v,w});
e[v].push_back({u,w});
}
dij(-1,-1);
// for(int i=1;i<=cnt;i++){
// cerr<<str[i]<<" \n"[i==cnt];
// }
double ans=DBL_MAX;
for(int i=1;i<cnt;i++){
// cerr<<str[i]<<' '<<str[i+1]<<endl;
dij(str[i],str[i+1]);
ans=min(ans,dist[n]);
}
if(ans==DBL_MAX)cout<<-1<<endl;
else printf("%.2lf",ans);
return 0;
}
严格的,可重复的次短路
例题:Roadblocks
思路:
次短路的话可能有两种:
- 把最短路中的最短段重复走了一个来回
- 走了一段非最短路中的
最一开始是正着按照这个思路来写,想参照前一类,分别切割最短路的每一段,然后跑许多遍最短路,如果和最短路相等就找到这个路线的最短边,double算贡献。但是突然发现这个最短边比较难求,所以反着想了一下。
想了一下对于这个图里的每一条边,总共有两种可能:
- 是最短路的一部分
- 非最短路的一部分
对于前者,我们可以考虑对其double,对于后者我们就强行将其放到次短路的路径中。
至于如何判断这条边是否为最短路的一部分:
dist(1,a)+len(a,b)+dist(b,n)==dist(1,n)就是最短路的一部分
code:
//P2865 严格但是可重复路径的次短路
//1.最短路但是把最短路中的最短段重复走了一次
//2.把最短路中的一段切割出去,强制不走这一条路的最短路(注意判断是否严格)
//对于最短路上的边,double一下
//对于非最短路上的边,放进去更换一下
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"
const int MAXN=5005;
const int inf=1e18;
#define f first
#define s second
int ans=inf,res;
int n,m;
struct edge{
int dis,v;
bool operator < (const edge &x)const{
return x.dis<dis;
}
};
priority_queue<edge>pq;
vector<pair<int,int> >e[MAXN];
int vis[MAXN];
int dis1[MAXN],dis2[MAXN];
void dij(){
for(int i=1;i<=n;i++){
dis1[i]=inf;
dis2[i]=inf;
}
dis1[1]=0;
pq.push({0,1});
while(!pq.empty()){
auto j=pq.top();
pq.pop();
int u=j.v;
if(vis[u])continue;
vis[u]=1;
for(auto i:e[u]){
int v=i.f,w=i.s;
if(dis1[u]+w<dis1[v]){
dis1[v]=dis1[u]+w;
if(!vis[v])pq.push({dis1[v],v});
}
}
}
for(int i=1;i<=n;i++){
vis[i]=0;
}
dis2[n]=0;
pq.push({0,n});
while(!pq.empty()){
auto j=pq.top();
pq.pop();
int u=j.v;
if(vis[u])continue;
vis[u]=1;
for(auto i:e[u]){
int v=i.f,w=i.s;
if(dis2[u]+w<dis2[v]){
dis2[v]=dis2[u]+w;
if(!vis[v])pq.push({dis2[v],v});
}
}
}
}
signed main(){
cin>>n>>m;
int u,v,w;
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
e[u].push_back({v,w});
e[v].push_back({u,w});
}
dij();
for(int i=1;i<=n;i++){
for(int j=0;j<e[i].size();j++){
int v=e[i][j].f,w=e[i][j].s;
if(dis1[i]+dis2[v]+w==dis1[n]){
res=dis1[n]+2*w;
}
else{
res=dis1[i]+dis2[v]+w;
}
ans=min(ans,res);
}
}
cout<<ans<<endl;
}
标签:图论,dist,int,短路,vis,MAXN,3.26,const
From: https://www.cnblogs.com/muyi-meow/p/18097828