思路:
将问题化简一下:
求x到y的最小距离(这里花费等价于距离)
那么使用4种基本模板:
由于是刚开始写,所以说明每个算法的基本逻辑,但是很明显做法2和做法4是优先考虑的(因为是优化版嘛)
这里用四个方法解决这道最简单的求单源路径最短问题
1.dijstra朴素版
//dijkstra朴素版
#include
#include
using namespace std;
const int N=2510,M=6200*2+10;
int d[N]; //d[i]=j表示点i距离源点的距离是j(源点就是起点)、
bool st[N]; //记录哪些点被找过
int h[N],e[M],ne[M],w[M]; //构建邻接表
int idx;
int n,c,start,End;//n个点,c条双向边,start是起点,end是终点
void add(int a,int b,int c)
{
e[idx]=b; //新节点
ne[idx]=h[a]; //新节点的下一个节点指向h[a]的下一个节点(头插法),h[a]是a的邻接表
//比如原来: a->c->d->null
//现在: a b->c->d->null
w[idx]=c; //新边的权值
h[a]=idx++; //然后h[a]指向新节点,就完成了a->b->c->d->null(原来a的下一个节点)
}
void dijkstra() //朴素dijkstra
{
//每次找到距离源点最近的点x,然后以x为中心更新其他的点(距离只要比原来小就更新)
memset(d,0x3f,sizeof d);
d[start]=0;
// for(int i=h[start];i!=-1;i=ne[i])
// {
// int j=e[i];
// d[j]=w[i];
// }
for(int i=0;i<n;i++) //每次都可以确定一个点到源点的距离最小,那么需要找n次,源点也算
{
int t=-1;
for(int j=1;j<=n;j++) //找到距离源点最近的点
{
if(!st[j]&&(t==-1||d[t]>d[j]))
t=j;
}
st[t]=true; //表示t点到源点的最短距离就此确定。
//这一步很巧秒,用反证法证明:此时t是所有点中距离源点最近的点
//如果t不是距离源点最近的点,那么一定有其他点经过若干边距离源点更近,
//但是此时t已经是距离源点最近的点了,单凡t或者其他点再经过任意一个边,到达源点
//的距离都一定大于此时的t到源点的距离(没有负边的情况)
//接下来是通过t更新距离源点更加近的点
for(int j=h[t];j!=-1;j=ne[j])
{
int k=e[j];
if(st[k])continue;
d[k]=min(d[k],d[t]+w[j]);
}
}
}
int main()
{
cin>>n>>c>>start>>End;
memset(h,-1,sizeof h); //初始化邻接表
for(int i=0;i<c;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
dijkstra();
cout<<d[End];
return 0;
}
2.优化版dijkstra
#include
#include
#include
#include
using namespace std;
const int N=2510,M=6200*2+10;
typedef pair<int,int> PII;
int h[N],e[M],ne[M],w[M],idx;
int d[N];
bool st[N];
int n,c,ts,td;
void add(int a,int b,int c)
{
e[idx]=b;
ne[idx]=h[a];
w[idx]=c;
h[a]=idx++;
}
void dijkstra()
{
memset(d,0x3f,sizeof d);
d[ts]=0;
priority_queue<PII,vector<PII>,greater<PII>>heap; //距离源点的距离被排好序,这是优化的唯一步骤
heap.push({0,ts}); //first是距离,second是节点编号
while(heap.size())
{
auto t=heap.top();
heap.pop();
int distance=t.first,ver=t.second;
if(st[ver])continue;
st[ver]=true;
for(int i=h[ver];i!=-1;i=ne[i])
{
int j=e[i];
if(st[j])continue;
if(distance+w[i]<d[j])
{
d[j]=distance+w[i];
heap.push({d[j],j});
}
}
}
}
int main()
{
cin>>n>>c>>ts>>td;
memset(h,-1,sizeof h);
while(c--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
dijkstra();
cout<<d[td];
return 0;
}
3.bellman_ford算法
说一句,可能不对,但是这个算法好像floyd算法啊,就是不停对边进行松弛
#include
#include
using namespace std;
const int N=2510,M=20000;
int h[N],ne[M],e[M],w[M],idx;
int d[N];
int backup[N];
int n,c,ts,td;
void add(int a,int b,int c)
{
e[idx]=b;
ne[idx]=h[a];
w[idx]=c;
h[a]=idx++;
}
void bellman_ford()
{
memset(d,0x3f,sizeof d);
d[ts]=0;
for(int i=1;i<=n;i++) //对n条边进行松弛
{
memcpy(backup,d,sizeof d);
for(int j=1;j<=n;j++) //对n个点都作为起点尝试松弛经过点j的点到源点的最短距离
{
for(int a=h[j];a!=-1;a=ne[a])
{
int b=e[a];
d[b]=min(d[b],backup[j]+w[a]);
}
}
}
}
int main()
{
cin>>n>>c>>ts>>td;
memset(h,-1,sizeof h);
while(c--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
bellman_ford();
cout<<d[td];
return 0;
}
4.spaf算法
Bellman_ford算法会遍历所有的边,但是有很多的边遍历了其实没有什么意义,我们只用遍历那些到源点距离变小的点所连接的边即可,只有当一个点的前驱结点更新了,该节点才会得到更新;因此考虑到这一点,我们将创建一个队列每一次加入距离被更新的结点。
#include
#include
#include
using namespace std;
const int N=2510,M=20000;
int h[N],ne[M],e[M],w[M],idx;
int d[N];
bool st[N];
int n,c,ts,td;
void add(int a,int b,int c)
{
e[idx]=b;
ne[idx]=h[a];
w[idx]=c;
h[a]=idx++;
}
void spfa()
{
memset(d,0x3f,sizeof d);
queue<int>q;
q.push(ts);
d[ts]=0;
st[ts]=true;
while(!q.empty())
{
int t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(d[j]>d[t]+w[i]) //如果从t到源点的距离加上t到j的距离小于j到源点的距离,那么进行松弛操作。
{
//注意,如果j在队列里面,不需要加入(队列里面的元素都是被其前驱点更新过的点,这些有用的点去继续更新其他点)
d[j]=d[t]+w[i];
if(!st[j])
{
st[j]=true;
q.push(j);
}
}
}
}
}
int main()
{
cin>>n>>c>>ts>>td;
memset(h,-1,sizeof h);
while(c--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
spfa();
cout<<d[td];
return 0;
}
任意选一个模板,找到点1到最远的那个点的距离
#include
#include
#include
#include
using namespace std;
const int N=110,M=410;
int h[N],e[M],ne[M],w[M],idx;
int d[N];
bool st[N];
int n,m;
void add(int a,int b,int c)
{
e[idx]=b;
ne[idx]=h[a];
w[idx]=c;
h[a]=idx++;
}
void spaf()
{
memset(d,0x3f,sizeof d);
queue<int>q;
q.push(1);
d[1]=0;
st[1]=true;
while(!q.empty())
{
int t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(d[j]>d[t]+w[i])
{
d[j]=d[t]+w[i];
if(!st[j])
{
st[j]=true;
q.push(j);
}
}
}
}
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
spaf();
int res=0;
for(int i=1;i<=n;i++)
{
if(d[i]>0x3f3f3f3f/2)
{
res=-1;
break;
}
else if(d[i]>res)
{
res=d[i];
}
}
cout<<res<<endl;
return 0;
}
floyd算法写的(yxc的方法)
#include
#include
#include
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
int d[N][N];
int main()
{
cin >> n >> m;
memset(d, 0x3f, sizeof d);
for (int i = 1; i <= n; i ++ ) d[i][i] = 0;
for (int i = 0; i < m; i ++ )
{
int a, b, c;
cin >> a >> b >> c;
d[a][b] = d[b][a] = min(d[a][b], c);
}
for (int k = 1; k <= n; k ++ )
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
int res = 0;
for (int i = 1; i <= n; i ++ )
if (d[1][i] == INF)
{
res = -1;
break;
}
else res = max(res, d[1][i]);
cout << res << endl;
return 0;
}
多源汇最短路问题:
标准做法:
1.floyd算法(n3)
2.使用n次dijkstra优化版(n* m *log2n)
3.使用 n次spaf算法(m * n)
思路:枚举所有点为起点,计算起点到所有牛的最短路径的和。
找出这些起点中之和最短的那个起点
#include
#include
#include
#include
using namespace std;
const int N=3000;
int h[N],e[N],ne[N],w[N],idx;
int d[N];
bool st[N];
int mm[N];
int n,p,c;//奶牛数 N,牧场数 P,牧场间道路数 C。
void add(int a,int b,int c)
{
e[idx]=b;
ne[idx]=h[a];
w[idx]=c;
h[a]=idx++;
}
void spaf(int start)
{
memset(st,false,sizeof st);
memset(d,0x3f,sizeof d);
queue<int>q;
q.push(start);
st[start]=true;
d[start]=0;
while(!q.empty())
{
int t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(d[j]>d[t]+w[i])
{
d[j]=d[t]+w[i];
if(!st[j])
{
st[j]=true;
q.push(j);
}
}
}
}
}
int main()
{
cin>>n>>p>>c;
for(int i=0;i<n;i++)
{
int x;
cin>>x;
mm[i]=x;
}
memset(h,-1,sizeof h);
while(c--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
int res=0x3f3f3f3f;
for(int i=1;i<=p;i++) //枚举每个点作为汇集点
{
spaf(i);
int sum=0;
for(int j=0;j<n;j++)
{
if(d[mm[j]]==0x3f3f3f3f)
{
sum=0x3f3f3f3f;
break;
}
sum+=d[mm[j]];
}
res=min(res,sum);
}
cout<<res;
return 0;
}
#include
#include
#include
using namespace std;
const int N=110;
int w[N][N],level[N];
int d[N];
bool st[N];
int n,m;
int dijkstra(int down,int up)
{
memset(d,0x3f,sizeof d);
memset(st,0,sizeof st);
d[0]=0;
for(int i=1;i<=n+1;i++)
{
int t=-1;
for(int j=0;j<=n;j++)
if(!st[j]&&(t==-1||d[t]>d[j]))
t=j;
st[t]=true;
for(int j=1;j<=n;j++)
{
if(level[j]>=down&&level[j]<=up)
d[j]=min(d[j],d[t]+w[t][j]);
}
}
return d[1];
}
int main()
{
cin>>m>>n;
memset(w,0x3f,sizeof w);
for(int i=1;i<=n;i++)
w[i][i]=0;
for(int i=1;i<=n;i++)
{
int price,cnt;
cin>>price>>level[i]>>cnt;
w[0][i]=min(w[0][i],price);
while(cnt--)
{
int id,cost;
cin>>id>>cost;
w[id][i]=min(w[id][i],cost);
}
}
int res=0x3f3f3f3f;
for(int i=level[1]-m;i<=level[1];i++)
res=min(res,dijkstra(i,i+m));
cout<<res;
return 0;
}
#include
#include
#include
using namespace std;
const int N=200010;
int h[N],ne[N],idx,e[N];
double w[N];
double d[N];
bool st[N];
int n,m,ts,td;
void add(int a,int b,double c)
{
e[idx]=b;
ne[idx]=h[a];
w[idx]=c;
h[a]=idx++;
}
void spaf()
{
queue<int>q;
d[ts]=1.0;
q.push(ts);
st[ts]=true;
while(!q.empty())
{
int t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(d[j]<d[t]*w[i])
{
d[j]=d[t]*w[i];
if(!st[j])
{
st[j]=true;
q.push(j);
}
}
}
}
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
double z=(100.0-c)/100;
add(a,b,z);
add(b,a,z);
}
cin>>ts>>td;
spaf();
printf("%.8lf",100/d[td]);
return 0;
}
#include
#include
#include
#include
using namespace std;
const int N=550;
int m,n;
bool g[N][N];
int d[N];
int stop[N];
int q[N];
void bfs() //找到距离源点最近的点
{
memset(d,0x3f,sizeof d);
int hh=0,tt=0;
d[1]=0;
q[0]=1;
while(hh<=tt)
{
int t=q[hh++];
for(int i=1;i<=n;i++)
{
if(g[t][i]&&d[i]>d[t]+1) //任意两个点的距离都被抽象为1
{
d[i]=d[t]+1;
q[++tt]=i;
}
}
}
}
int main()
{
cin>>m>>n;
string line;
getline(cin,line); //读取换行
while(m--)
{
getline(cin,line);
stringstream ss(line);
int cnt=0,p;
while(ss>>p)stop[cnt++]=p;
for(int i=0;i<cnt;i++)
for(int j=i+1;j<cnt;j++)
g[stop[i]][stop[j]]=true;
}
bfs();
if(d[n]==0x3f3f3f3f)puts("NO");
else cout<<max(d[n]-1,0)<<endl;
return 0;
}