单源最短路劲算法
Dijkstra算法:
贪心思想:每次找到一个最优点,然后遍历最优点的边,更新维护一个dist数组(dist代表点到各个点的目前最优距离)。
用数组简单是简单,但是遇到大量数据,被安排的明明白白,但还是把两中都写上把。
数组版(简单未优化,新手看)
/*未优化,入门简单版*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#include<algorithm>
#define Max 1005
#define max(a,b) a>b?a:b;
#define inf 0x3f3f3f3f
using namespace std;
int a[Max][Max]; //各个定点的距离
bool vis[Max]; //记录是否走过
int dist[Max]; //目前最优距离
int n; //点的数量
/*数组版的效率可能低了点...*/
void dijkstra(int num)
{
dist[num]=0;//自己是0
for(int i=1;i<n;i++) //一共需要找多少个点
{
int min=inf,u=-1;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&dist[j]<min) //找到最小的点
{
min=dist[j];
u=j;
}
}
vis[u]=true;//走过
for(int j=1;j<=n;j++)
{
if(min+a[u][j]<dist[j])
{
dist[j]=a[u][j]+min;
}
}
}
}
int main()
{
int beg,end,m,x,y,w;
memset(a,inf,sizeof(a)); //记得初始化这几个数组
memset(vis,false,sizeof(vis));
memset(dist,inf,sizeof(dist));
scanf("%d%d%d%d",&n,&m,&beg,&end); //依次为点,边,开始的点,结束的点
while(m--)
{
scanf("%d%d%d",&x,&y,&w);
a[x][y]=w;
a[y][x]=w;
}
dijkstra(beg);
printf("%d\n",dist[end]);
return 0;
}
优化策略:每次查找最小点的时候可以用堆优化(我使用STL的优先队列,不懂的可以先百度),当数据比较大,有事稀疏图的时候数组会炸,所有使用结构体保存边,利用一个head数组作为索引(好像有人叫向前星??,我觉得就是和链表一样头插法)
稀疏图时间复杂度:O(ElgV)
个人目前使用的Dijkstra模板,别说模板麻烦,遇到有些题卡时间就知道了,也可以自己找模板:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#define Max 100010
#define max(a,b) a>b?a:b;
#define min(a,b) a>b?b:a;
#define inf 0x3f3f3f3f
/*数据存储优化,用数组储存尽量,但是不能太大...AC! 标准版868ms 弱化版429ms*/
using namespace std;
bool vis[Max];
int dist[Max];
int max_num;
struct Edge{
int next; //指向下一个元素
int to; //点
int dis;//距离
}edge[200010];
int num_edge;
int head[Max];//初始化为0
void add_edge(int x,int y,int data) //头插法
{
edge[++num_edge].next=head[x];
edge[num_edge].to=y;
edge[num_edge].dis=data;
head[x]=num_edge;
}
struct heap{ //构建优先队列
int data;
int weight;
};
struct cmp{ //优先队列 比较函数
bool operator()(struct heap a,struct heap b)
{
return a.weight>b.weight;
}
};
priority_queue <struct heap,vector<heap>,cmp> q;
struct node{
int data;
int weight;
struct node *next;
};
int n;
void dijkstra(int num)
{
dist[num]=0;
max_num=n;
struct heap h;
h.data=num;
h.weight=0;
num_edge=0;
q.push(h);
while(q.size())
{
int min=inf,u=-1;
// for(int j=1;j<=max_num;j++) //每次查找都需要n时间,
// {
// if(!vis[j]&&dist[j]<=min)
// {
// min=dist[j];
// u=j;
// }
// }
h=q.top();
q.pop();
min=h.weight;
u=h.data;
if(vis[u])
continue;
vis[u]=true;
for(int i=head[u];i;i=edge[i].next)
{
if(min+edge[i].dis<dist[edge[i].to])
{
dist[edge[i].to]=min+edge[i].dis;
if(vis[edge[i].to]) //已经确定了最优点了
continue;
h.data=edge[i].to; //点
h.weight=dist[edge[i].to];
q.push(h);
}
}
}
}
void show(int a[],int n)
{
for(int i=1;i<n;i++)
{
// if(a[i]==1061109567) //我设置的和它不一样...
// printf("2147483647");
// else
printf("%d",a[i]);
if(i!=n-1)
printf(" ");
}
printf("\n");
}
int main()
{
int m,x,y,weight,data;
memset(vis,false,sizeof(vis));
memset(dist,inf,sizeof(dist));
memset(head,0,sizeof(head));
scanf("%d%d%d",&n,&m,&data);
while(m--)
{
scanf("%d%d%d",&x,&y,&weight);
if(x==y)
continue;
else
{
add_edge(x,y,weight);
}
}
dijkstra(data);
show(dist,n+1);
return 0;
}
下面题都能过
SPFA算法:
介绍:Bellman-Ford算法 的队列优化算法,等等。自己看吧。
emmm,其实我也是今天刚学,其实它的复杂度比Dijkstra大,但是用的人多,主要是它容易写,代码量小。还有个优点判断环,还有能计算负权值的情况。
PS;有时候出题人会故意卡SPFA算法的哦,所以能用Disjksta 最好用Dijkstra求。
贴一个今天刚写的模板:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#include<algorithm>
#define Max 10001
#define max(a,b) a>b?a:b;
#define min(a,b) a>b?b:a;
using namespace std;
/*按照LG1339写的*/
queue<int> q;
int d[Max];
int cnt[Max];//记录走过了多少次
bool inque[Max]; //在队列中的信息
struct Node{
int t;
int dist;//距离
};
vector<Node> g[Max];
int n;
bool spfa(int s) //从s开始
{
memset(d,0x3f3f3f3f,sizeof(d));
memset(inque,0,sizeof(inque));
memset(cnt,0,sizeof(cnt));
d[s]=0;
q.push(s);
inque[s]=true; //记录
cnt[s]++;
while(q.size())
{
int u=q.front();
q.pop();
inque[u]=false; //出去
int nun=g[u].size(); //提前把记录弄出来,不然警告。。。
for(int i=0;i<nun;i++)
{
int v=g[u][i].t;
int w=g[u][i].dist;
if(d[u]+w<d[v])
{
d[v]=d[u]+w;
if(inque[v]==false) //不在队列里面
{
q.push(v); //入队
cnt[v]++;
if(cnt[v]>n)
return false;
inque[v]=true;
}
}
}
}
return true;
}
int main()
{
int m,st,end;
int x,y,w;
scanf("%d%d%d%d",&n,&m,&st,&end); //点、边、开始、结束
struct Node temp;
while(m--)
{
scanf("%d%d%d",&x,&y,&w);
temp.t=x;
temp.dist=w;
g[y].push_back(temp);
temp.t=y;
g[x].push_back(temp);//两边都要走
}
if(spfa(st))
printf("%d\n",d[end]);
else
printf("有环。\n");
return 0;
}
多源最短路劲问题
Floyd算法
核心代码5行,简单吧:
for(k=1;k<=n;k++) //一共有多少个点,每次进入一个点
for(i=1;i<=n;i++) //遍历所有的点
for(j=1;j<=n;j++)
if(e[i][j]>e[i][k]+e[k][j]) //松弛操作
e[i][j]=e[i][k]+e[k][j];
AC代码:
#include<stdio.h>
#include<string.h>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#define Max 210
#define inf 0x3f3f3f3f
#define min(a,b) a>b?b:a;
using namespace std;
int t[Max];//村庄
int m[Max][Max]; //路径的值
int main()
{
int n,mm,x,y,data,k=0;
memset(m,inf,sizeof(m));
scanf("%d%d",&n,&mm);
for(int i=0;i<n;i++)
{
scanf("%d",&t[i]);
m[i][i]=0;
}
while(mm--)
{
scanf("%d%d%d",&x,&y,&data);
if(x==y)
continue;
m[x][y]=data; //两边都可以走哦
m[y][x]=data;
}
int q;
scanf("%d",&q);
k=0;
while(q--) //询问
{
scanf("%d%d%d",&x,&y,&data);
while(t[k]<=data&&k<=n) //注意,村庄恢复的时间题目给的升序,不需要排
{
for(int i=0;i<n;i++) //把这个点进来后更新所有的点
{
for(int j=0;j<n;j++)
{
m[i][j]=min(m[i][j],m[i][k]+m[k][j]);
}
}
k++;
}
if(m[x][y]==inf||t[x]>data||t[y]>data) //时间不对或者不能到达
printf("-1\n");
else
printf("%d\n",m[x][y]);
}
return 0;
}
暂时到这了,有问题请在下面留言。
时间越来越紧了,大批算法需要学习,暑假加油啊!ACM!!!