最短路径
首先呢,最短路蒟蒻会的板子只有3个,且都为朴素版qwq
1.floyd(n^3)
就是个暴力转移的算法,注意k,i,j顺序如果不是k,i,j正确性会出问题
板子在这里
代码如下
#include<bits/stdc++.h>
#define int long long
using namespace std;
int dis[210][210];
signed main(){
int n,m;
cin>>n>>m;
memset(dis, 0x3f, sizeof(dis));
for (int i = 1; i <= m; i ++) {
int x, y, z;
cin>>x>>y>>z;
dis[x][y] = z;
dis[y][x] = z;
}
for (int k=1;k<=n;k++) {
for (int i = 1; i<=n;i++) {
for (int j=1; j<=n;j++) {
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
for (int i=1;i <=n; i++) {
for (int j=1; j <=n; j++) {
if (i != j){
cout<<dis[i][j]<<" ";
}
else{
cout<<0<<" ";
}
}
cout << endl;
}
return 0;
}
推荐一道水题
只需要用floyd+O2就可以过qwq
代码如下
#include<bits/stdc++.h>
#define int long long
using namespace std;
int f[1001][1001];
int n,m,u,v,w,ans=0;
signed main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
f[i][j]=0x7fffffff;
}
}
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
f[u][v]=min(f[u][v],w);
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(f[i][j]>f[i][k]+f[k][j]){
f[i][j]=f[i][k]+f[k][j];
}
}
}
}
for(int i=2;i<=n;i++){
ans+=f[1][i]+f[i][1];
}
cout<<ans;
return 0;
}
2.spfa(出题人没喝那一般就是nm)
板子
算法原理:大概就是不断取出队首的点进行松弛操作(其实就是更新,我也不知道为啥叫这个鬼名),如果可以松弛且不在队列中那就把它加入队列,像这样循环每次取出队首元素更新直到将队列内的元素都弹空
优点可以判负环,跑负边权,但过于容易被卡,只需要一个随机网格图(在网格图中走错一次路可能导致很高的额外开销),或者一个构造过的链套菊花(使得队列更新菊花的次数非常高)即可(from网络世界),这个是真的,所以我们要学习如何优化,但我还不会...,这个等以后会再次更新
代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=0x7fffffff;//2147483647
const int maxm=5e5+5;
const int maxn=1e4+5;
int n,m,s;
int cnt;
int dis[maxn];
int vis[maxn];
int head[maxm];
struct node{
int nxt,to,dis;
}p[maxm];
void add(int u,int v,int w){
p[++cnt].nxt=head[u];
p[cnt].to=v;
head[u]=cnt;
p[cnt].dis=w;
}
void spfa(){
queue<int>q;
for(int i=1;i<=n;i++){
dis[i]=inf;
vis[i]=0;
}
q.push(s);
dis[s]=0;
vis[s]=1;
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=p[i].nxt){
int v=p[i].to;
if(dis[v]>dis[u]+p[i].dis){
dis[v]=dis[u]+p[i].dis;
if(vis[v]==0){
vis[v]=1;
q.push(v);
}
}
}
}
}
signed main(){
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
int uu,vv,ww;
cin>>uu>>vv>>ww;
add(uu,vv,ww);
}
spfa();
for(int i=1;i<=n;i++){
if(s==i){
cout<<0<<" ";
}
else{
cout<<dis[i]<<" ";
}
}
return 0;
}
//diji增加了一个贪心操作,即每回用之前的最短路来更新,而有负边权的话可能需要从不是最短路的那条更新,而spfa则是能入队就入队
3.dijkstra(mlogn好像是,如果错了请大佬指正)
算法原理:个人觉得就是在spfa基础上加个贪心,用之前的最短路来更新,而有负边权时贪心正确性出错,因为它显然是短视的
板子
代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,s;
const int mod=(1<<31)-1;
const int maxn=5e5+5;
int diss[maxn];//源点到当前点的最小路径
int to[maxn];
int head[maxn];
int nxt[maxn];
int v[maxn];
int cnt;
struct node{
int id,dis;
friend bool operator < (node a,node b){
return a.dis>b.dis;//小根堆
}
};
void add(int x,int y,int z){
to[++cnt]=y;//儿子
nxt[cnt]=head[x];//上一条边
head[x]=cnt;//边数
v[cnt]=z;
}
priority_queue<node>q;
void dj(int s){//起点
for(int i=1;i<=n;i++){//赋初值
diss[i]=mod;
}
diss[s]=0;//每回挑出离源点最近的点
q.push(node{s,0});
while(!q.empty()){
node jt=q.top();//取出堆顶元素
q.pop();
int id=jt.id;
int disss=jt.dis;
if(disss!=diss[id]){//应该就是这里会被负边权干掉,用vis数组也一样
continue;//个人理解,如果后面已经被再次更新,就证明可以直接进入下一次
}
for(int i=head[id];i;i=nxt[i]){
if(diss[to[i]]>diss[id]+v[i]){
diss[to[i]]=diss[id]+v[i];
q.push(node{to[i],diss[to[i]]});
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
}
dj(s);
for(int i=1;i<=n;i++){
cout<<diss[i]<<" ";
}
return 0;
}
例题
汽车行驶问题
简单来说就是建k层图,走的步数不同在不同层,而边权存储走这一步所需价值
代码如下
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,a,b,c;
int s;
int cnt;
const int inf=0x3f3f3f3f;
const int maxn=1e5+5;
int dis[maxn*3];
int vvv;
int vis[maxn*15];
int to[maxn*15];
int nxt[maxn*15];
int v[maxn*15];
int head[maxn*15];
struct node{
int id,dis;
friend bool operator < (node a,node b){
return a.dis>b.dis;
}
}p[maxn*15];
void add(int x,int y,int z){
to[++cnt]=y;
nxt[cnt]=head[x];
v[cnt]=z;
head[x]=cnt;
}
void spfa(int s){
queue<int>q;
q.push(s);
dis[s]=0;
vis[s]=1;
while(!q.empty()){
int a=q.front();
vis[a]=0;
q.pop();
for(int i=head[a];i;i=nxt[i]){
int vv=to[i];
if(dis[vv]>dis[a]+v[i]){
dis[vv]=dis[a]+v[i];
if(!vis[vv]){
q.push(vv);
vis[vv]=1;
}
}
}
}
}
int tr(int i,int j,int k){
return k*n*n+(i-1)*n+j;
}
bool check (int i,int j){
return 1<=i&&i<=n&&1<=j&&j<=n;
}
signed main(){
ios::sync_with_stdio(false);
cin>>n>>k>>a>>b>>c;
memset(dis,inf,sizeof(dis));
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int opt;
cin>>opt;
if(opt){
for(int jj=1;jj<=k;jj++){
add(tr(i,j,jj),tr(i,j,0),a);
}
if(check(i+1,j)){
add(tr(i,j,0),tr(i+1,j,1),0);
}
if(check(i,j-1)){
add(tr(i,j,0),tr(i,j-1,1),b);
}
if(check(i-1,j)){
add(tr(i,j,0),tr(i-1,j,1),b);
}
if(check(i,j+1)){
add(tr(i,j,0),tr(i,j+1,1),0);
}
}
else{
for(int jj=1;jj<=k;jj++){
add(tr(i,j,jj),tr(i,j,0),a+c);
}
for(int kk=0;kk<k;kk++){
if(check(i+1,j)){
add(tr(i,j,kk),tr(i+1,j,kk+1),0);
}
if(check(i,j-1)){
add(tr(i,j,kk),tr(i,j-1,kk+1),b);
}
if(check(i-1,j)){
add(tr(i,j,kk),tr(i-1,j,kk+1),b);
}
if(check(i,j+1)){
add(tr(i,j,kk),tr(i,j+1,kk+1),0);
}
}
}
}
}
spfa(tr(1,1,0));
int ans=1e18+2;
for(int kk=1;kk<=k;kk++){
ans=min(ans,dis[tr(n,n,kk)]);
}
cout<<ans;
return 0;
}
the end!!!