求单源最短路径(边的权值非负) -----Dijsktra算法
所谓单源最短路径就是固定一个顶点为源点 求源点到其他每个顶点的最短路径
在Dijkstra 算法里,为了求源点v0 到其他各顶点vi 的最短路径及其长度,需要设置3 个数组:
a) dist[n]:dist[i]表示当前找到的从源点v0 到终点vi 的最短路径的长度,初始时,dist[i]
为Edge[v0][i],即邻接矩阵的第v0 行。
b) S[n]:S[i]为0 表示顶点vi 还未加入到集合S 中,S[i]为1 表示vi 已经加入到集合S 中。
初始时,S[v0]为1,其余为0,表示最初集合S 中只有顶点v0。
c) path[n]:path[i]表示v0 到vi 的最短路径上顶点vi 的前一个顶点序号。采用“倒向追踪”
的方法,可以确定v0 到顶点vi 的最短路径上的每个顶点。
在Dijkstra 算法里,重复做以下3 步工作:
1) 在数组dist[n]里查找S[i] != 1,并且dist[i]最小的顶点u;
2) 将S[u]改为1,表示顶点u 已经加入进来了;
3) 修改T 集合中每个顶点vk 的dist 及path 数组元素值:当S[k] != 1,且顶点u 到顶点vk
有边(Edge[u][k]<MAX),且dist[u] + Edge[u][k] < dist[k],则修改dist[k]为dist[u] +
Edge[u][k],修改path[k]为u。
ZOJ 1298 POJ 1135
多米诺骨牌效应
计算最后倒下的是哪一张牌 在什么时间倒下
游戏中有一些关键牌 当关键牌倒下时 连接这张关键牌的所有行都开始倒下
思路:
最后倒下的牌有两种情形:
① 最后倒下的牌是关键牌,其时间及位置就是第1 张关键牌到其他关键牌中最短路
径的最大值及对应的关键牌;
② 最后倒下的牌是两张关键牌之间的某张普通牌,其时间为这两张
关键牌倒下时间的一半再加上及这一行倒下时间的一半,位置为这两张牌之间的某张普通牌(不
一定恰好是该行正中间的那张牌,但题目并不需要具体求出是哪张牌)。
本题的方法步骤如下:
a) 先计算每一张关键牌倒下的time[i]。这需要利用Dijkstra 算法求第1 张关键牌到其他每
张关键牌的最短路径。然后取time[i]的最大值,设为maxtime1。
b) 计算每一行完全倒下的时间。设每一行的两端的关键牌为i 和j,则这一行完全倒下的时
间为(time[i] + time[j] + Edge[i][j])/2.0,其中Edge[i][j]为连接第i、j 两张关键牌的行倒下
所花的时间。取所有行完全倒下时间的最大值,设为maxtime2。
c) 如果maxtime2 > maxtime1,则是第②种情形;否则是第①种情形。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#define eps 1e-8
#define op operator
#define MOD 10009
#define MAXN 100100
#define INF 0x7fffffff
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define FOV(i,a,b) for(int i=a;i>=b;i--)
#define REP(i,a,b) for(int i=a;i<b;i++)
#define REV(i,a,b) for(int i=a-1;i>=b;i--)
#define MEM(a,x) memset(a,x,sizeof a)
#define ll __int64
using namespace std;
int n,m;
int edge[510][510];
int s[510];
int time[510];//代表第i张牌倒下的时间
int cs;
void Dijkstra()
{
for(int i=0;i<n;i++)
{
time[i]=edge[0][i];
s[i]=0;
}
time[0]=0; s[0]=1;
for(int i=0;i<n-1;i++)// 从顶点0开始确定n-1条最短路径
{
int mi=INF, u=0;
for(int j=0;j<n;j++)
{
if(!s[j]&&time[j]<mi)
{
u=j; mi=time[j];
}
}
s[u]=1;
for(int j=0;j<n;j++)
{
if(!s[j]&&edge[u][j]<INF&&time[u]+edge[u][j]<time[j])
{
time[j]=time[u]+edge[u][j];
}
}
}
double mx1=-(double)INF;
int pos;
for(int i=0;i<n;i++)
{
if(time[i]*1.0>mx1)
{
mx1=time[i]*1.0; pos=i;
}
}
double mx2=-(double)INF,t; int pos1,pos2;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
t=(time[i]+time[j]+edge[i][j])/2.0;
if(edge[i][j]<INF&&t>mx2)
{
pos1=i; pos2=j;
mx2=t;
}
}
}
printf("System #%d\n",cs++);
printf("The last domino falls after ");
if(mx1>=mx2)
{
printf("%.1f seconds, at key domino %d.\n",mx1,pos+1);
}
else
{
printf("%.1f seconds, between key dominoes %d and %d.\n",mx2,pos1+1,pos2+1);
}
puts("");
}
int main()
{
//freopen("ceshi.txt","r",stdin);
cs=1;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0)
break;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
edge[i][j]=INF;
for(int i=0;i<m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
u--; v--;
edge[u][v]=edge[v][u]=w;
}
Dijkstra();
}
return 0;
}
zoj 2750 成语接龙游戏
给出n个成语 每个成语由若干词构成 要将成语连接起来 下一个成语的第一个词要跟上一个成语的最后一个词
在词典中查找成语需要花时间 所花的时间为该成语前的一个数字的值
首先就是建图 如果一个成语的最后一个词跟另外一个成语的第一个词相同就有一条边
边的权值为所花的时间 求最短路即可
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#define eps 1e-8
#define op operator
#define MOD 10009
#define MAXN 1010
#define INF 0x7fffffff
#define MEM(a,x) memset(a,x,sizeof a)
#define ll __int64
using namespace std;
struct node
{
char fro[5],bak[5];
int val;
};
node no[MAXN];
int Edge[MAXN][MAXN];
int dis[MAXN];
int S[MAXN];
int main()
{
//freopen("ceshi.txt","r",stdin);
int n;
while(scanf("%d",&n)!=EOF)
{
if(n==0) break;
for(int i=0;i<n;i++)
{
char ch[1000];
scanf("%d%s",&no[i].val,ch);
int len=strlen(ch);
for(int k=0,j=len-1;k<4;k++,j--)
{
no[i].fro[k]=ch[k];
no[i].bak[3-k]=ch[j];
}
no[i].fro[4]='\0';
no[i].bak[4]='\0';
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
Edge[i][j]=INF;
if(i==j) continue;
if(strcmp(no[i].bak,no[j].fro)==0)
{
Edge[i][j]=no[i].val;
}
}
}
for(int i=0;i<n;i++)
{
dis[i]=Edge[0][i];
S[i]=0;
}
//Dijkstra
S[0]=1;dis[0]=0;
for(int i=0;i<n-1;i++)
{
int mi=INF,u=0;
for(int j=0;j<n;j++)
{
if(!S[j]&&dis[j]<mi)
{
u=j; mi=dis[j];
}
}
S[u]=1;
for(int j=0;j<n;j++)
{
if(!S[j]&&Edge[u][j]<INF&&dis[u]+Edge[u][j]<dis[j])
{
dis[j]=dis[u]+Edge[u][j];
}
}
}
if(dis[n-1]==INF)
puts("-1");
else printf("%d\n",dis[n-1]);
}
return 0;
}