单源最短路和多源最短路
本来我今天要是搞懂了线段树我就想写线段树的模板,还是要几天去消化一下里面的道理。所以今天就炒一炒冷饭吧,嘻嘻。可能大家都了解这些代码,毕竟是数据结构的主要内容,但是自己实现起来还是不一样的,可以敲一敲我发的题目。
多源最短路 弗洛伊德算法
先发题目链接 hdu 2066 戳这里
先写这个主要是这个思路比较清晰,容易写一点,缺点就是时间复杂度比较高,如果不去剪枝的话很容易超时,不过剪枝的难度不高,所以总体来说求所有点的最短路径用这个就对了。
这个算法的核心思想就是一个三重循环,最外层的循环我们可以把它叫做中间点(意思就是 如果我们要求两个点的最短距离 需要判断这两个点的最短路程需不需要这这第三个点的参与),里面的一个二重循环你可以理解为起点和终点。(所以按照2066 可以敲一下)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
#define MAXN 5000
int d[1001][1001]; /*存信息*/
int main()
{
int t,s,q;
while(cin>>t>>s>>q)
{
int c[10001],e[10001];
int min=10000000,maxd=-1;
for(int i=0;i<1001;i++)
for(int j=0;j<1001;j++)
{ if(i==j)
d[i][j]=0;
else
d[i][j]=MAXN;
} /*初始化图信息*/
for(int i=0;i<t;i++)
{ int a,b,time;
cin>>a>>b>>time;
if(b>maxd)
maxd=b;
if(a>maxd)
maxd=a;
/*因为该题没说图的边界问题,所以只能找最远的点为边界了*/
if(d[a][b]>time)
{
d[a][b]=time;
d[b][a]=time;
}
/*考虑重边的情况,找最小的边*/
}
for(int k=0;k<=maxd;k++)
{
for(int i=0;i<=maxd;i++)
{
if(d[i][k]!=MAXN)
for(int j=0;j<=maxd;j++)
if(d[i][j]>(d[k][j]+d[i][k]))
d[i][j]=d[k][j]+d[i][k];
}
}
/*弗洛伊德算法*/
for(int i=0;i<s;i++)
cin>>c[i];
for(int j=0;j<q;j++)
cin>>e[j];
for(int i=0;i<s;i++)
for(int j=0;j<q;j++)
if(d[c[i]][e[j]]<min&&d[c[i]][e[j]]!=0)
min=d[c[i]][e[j]];
cout<<min<<endl;
/*最后找一条最短的输出*/
}
}
类似的还有一道 hdu 1599 戳这里不过这一道题需要一个弗洛伊德最小环的判定定理,想了解的可以自己百度。
单源最短路径 迪杰特斯拉算法
单源最短我还没敲过,所以特意去敲了一道题(我自己都有点爱我自己)
hdu 2544 戳这里
具体思想
- 确定你要求的起点。
- 按照你的起点初始化你的距离信息(就是起点到各个点的距离,如果到不了就设为一个较大值表示为无法到达)。
- 然后选取一个最小的边,连接起来,再修改距离的信息(如果起点经过该点再到终点的距离 小于 从起点到终点的距离就修改信息。
- 最后当遍历完所有点以后,数组里面的信息就是最短路径了。
下面是AC代码 注意(无穷大值一定要蛮大 不然报错)
#include<iostream>
#include<string.h>
#include<stack>
#include<queue>
#include<bits/stdc++.h>
using namespace std;
#define MAXN 200000
int map1[500][500];
int d[500];
int vis[500];
int main()
{
int n,m;
while(cin>>n>>m)
{
if(n==0&&m==0)
break;
memset(d,0,sizeof(d));
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
map1[i][j]=MAXN;
for(int i=1;i<=m;i++)
{
int a,b,c;
cin>>a>>b>>c;
if(map1[a][b]==0)
{
map1[a][b]=c;
map1[b][a]=c;
}
else if(c<map1[a][b])
{
map1[a][b]=c;
map1[b][a]=c;
}
}
for(int i=1;i<=n;i++)
d[i]=map1[1][i];
for(int i=1;i<=n;i++)
{
int temp=MAXN;
int k;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&d[j]<temp)
{
temp=d[j];
k=j;
}
}
vis[k]=1;
for(int j=1;j<=n;j++)
d[j]=min(d[j],d[k]+map1[k][j]);
}
cout<<d[n]<<endl;
}
}
最小生成树 克鲁斯卡尔算法
为什么不讲prime 因为我觉的那个方法不好理解,还容易和最短路径搞混,所以我超级讨厌用那个算法,如果想学prime 请出门右转。
hdu 1301 戳这里
具体思路
1.思路就一点 把你的所有路径的大小都排个序(升序),然后不断选最小边(如果成环就要舍弃那条要成环的边),并且用一个变量去记住累加的权值,最后输出就行了(关于判断环的方法,我采用了一点并查集的思想)不会并查集的百度,或者看代码自己理解
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
#define MAXN 5000
int map1[1001][1001];
int f[1001];
int n,m;
char a;
struct node{
int s;
int e;
int value;
};
bool cmp(node x,node y)
{
return x.value<y.value;
}
int find(int x)
{ int r=x;
while(r!=f[r])
r=f[r];
int i=x,j;
while(i!=r)
{
j=f[i];
f[i]=r;
i=j;
}
return r;
}
void join(int x,int y)
{
int fx=find(x);
int fy=find(y);
if(fx!=fy)
f[fx]=fy;
}
int main()
{
while(cin>>n&&n!=0)
{int k=0;
int cost=0;
node p[1001];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(i==j)
map1[i][j]==0;
else
map1[i][j]=MAXN;
}
for(int i=0;i<n-1;i++)
{
cin>>a>>m;
for(int j=0;j<m;j++)
{ char b;
int value;
cin>>b>>value;
map1[(int)(a-65)][(int)(b-65)]=value;
map1[(int)(b-65)][(int)(a-65)]=value;
}
}
for(int i=0;i<n;i++)
f[i]=i;
for(int i=0;i<n;i++)
for(int j=0;j<=i;j++)
{
if(map1[i][j]!=MAXN&&map1[i][j]!=0)
{
p[k].s=i;
p[k].e=j;
p[k].value=map1[i][j];
k++;
}
}
sort(p,p+k,cmp);
for(int i=0;i<k;i++)
{
if(find(p[i].s)!=find(p[i].e))
{
join(p[i].s,p[i].e);
cost+=p[i].value;
}
}
cout<<cost<<endl;
}
}
记住好吗!!!!!!!!