朴素版Dijkstra算法
核心思路:
1.循环n次,每次找到到起点距离最小且不在已确定集合中的的点
2.将找到的点放入集合中
3.用找到的点来更新其他所有未在集合之中的点
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m;
const int N = 510,M=1e5+10;
int g[N][N];
int dist[M],st[N];
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;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]);
}
}
if(dist[n]==0x3f3f3f3f) return -1;
else return dist[n];
}
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;
g[a][b]=min(g[a][b],c);
}
int t=dijkstra();
cout<<t;
return 0;
}
堆优化版的Dijkstra算法
核心思路:
1.将起点放入队列中(优先队列,小数在前)
2.取出队列中的点来更新他这一条链表上的点,如果更新了,就将更新的点放入队列中
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int,int> PII;
const int N=2e5+10;
int h[N],e[N],w[N],ne[N],idx;
int n,m;
int st[N];
int dist[N];
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
priority_queue<PII,vector<PII>,greater<PII>> q;//小根堆
q.push({0,1});//0表示距离,1表示点的位置
while(q.size())
{
auto t=q.top();
q.pop();
int ver=t.second,distance=t.first;
if(st[ver]) continue;
st[ver]=true;
for(int i=h[ver];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>w[i]+distance)
{
dist[j]=w[i]+distance;
q.push({dist[j],j});
}
}
}
if(dist[n]==0x3f3f3f3f) return -1;
else return dist[n];
}
int main()
{
cin>>n>>m;
memset(h, -1, sizeof h);
while (m -- )
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
int t=dijkstra();
cout<<t;
return 0;
}
spfa算法
求最短路
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int n,m;
const int N = 2e5+20;
int h[N],w[N],ne[N],e[N],idx;
bool st[N];
int dist[N];
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int spfa()
{
memset(dist, 0x3f ,sizeof dist);
queue<int> q;
q.push(1);
st[1]=true;
dist[1]=0;
while(q.size())
{
auto t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
if(!st[j])
{
st[j]=true;
q.push(j);
}
}
}
}
return dist[n];
}
int main()
{
cin>>n>>m;
memset(h, -1, sizeof h);
while (m -- ){
int a,b,c;
cin>>a>>b>>c;
add(a, b, c);
}
if(spfa()>0x3f3f3f/2) cout<<"impossible";
else cout<<dist[n];
return 0;
}
求负环
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int n,m;
const int N = 2e5+20;
int h[N],w[N],ne[N],e[N],idx;
bool st[N];
int dist[N];
int cnt[N];
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
bool spfa()
{
memset(dist,0x3f,sizeof dist);
queue<int> q;
for(int i=1;i<=n;i++)
{
st[1] = true;
q.push(i);
}
dist[1]=0;
while(q.size())
{
auto t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
cnt[j]=cnt[t]+1;
if(cnt[j]>n) return true;
if(!st[j])
{
st[j]=true;
q.push(j);
}
}
}
}
return false;
}
int main()
{
cin>>n>>m;
memset(h, -1, sizeof h);
while (m -- ){
int a,b,c;
cin>>a>>b>>c;
add(a, b, c);
}
if(spfa()) cout<<"Yes";
else cout<<"No";
return 0;
}
bellman_ford算法
核心思路:
用结构体来存边
1.循环k次,每次先将dist[ ]数组备份
2.在每次循环中更新更新距离,用起点到点A的距离加上A到B的距离
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510,M=1e5+10;
int n,m,k;
struct node
{
int a,b,w;
};
int dist[N],bf[N];
node edge[M];
int bellman_ford()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=1;i<=k;i++)
{
memcpy(bf,dist,sizeof dist);
for(int j=1;j<=m;j++)
{
auto e=edge[j];
dist[e.b]=min(dist[e.b],bf[e.a]+e.w);
}
}
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,b,c};
}
int t=bellman_ford();
if(t>0x3f3f3f3f/2) cout<<"impossible";
else cout<<t;
return 0;
}
prim算法求最小生成树
核心思路:
1.初始化所有距离为正无穷
2.for(n次)
{
每次找到不在集合中的距离最小的点
如果这个点的距离为正无穷,即这个点与已知集合不连通,即无法形成最小生成树
否则:{
将总路径加上这段距离
将其放入集合中
for()
{
更新距离为到最小距离的点和本身距离的小值
}
}
}
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510,INF=0x3f3f3f3f;
int n,m;
int g[N][N],dist[N];
bool st[N];
int prim()
{
memset(dist,0x3f,sizeof dist);
int res=0;
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
{
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
}
if(i&&dist[t]==INF) return INF;
if(i) res+=dist[t];
st[t]=true;
for(int j=1;j<=n;j++)
{
dist[j]=min(dist[j],g[t][j]);
}
}
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;
g[a][b]=g[b][a]=min(g[a][b],c);
}
int t=prim();
if(t==INF) puts("impossible");
else cout<<t;
}
二分
整数二分(如果在选择语句中mid+1则mid=l+r>>1;否则mid=l+r+1>>1)
int l,r;
bool check(int x)//检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用
int bsearch_1(int l,int r)
{
while(l<r)
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用
int bsearch_2(int l,int r)
{
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
return l;
}
浮点数二分
//精确到小数点后x位
int l,r;
double check(double x)
double bsearch(double l,double r)
{
while(r-l>10^(-(x+2)))
{
double mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
return l;
}