题目1008:最短路径问题
两层循环时,外层用i,内层就千万别再用i,
用j,k神马的都好!!!!!
WA检查半天竟然是这问题...巨汗。。。。。。。
法1:dij法。求单源路径时,带上花费。
/*
图的邻接矩阵存储+dij修改(3各要素初始化:边+标记+单源路径) 解决重边问题
*/
#include <stdio.h>
int n,m; //n各城市,m条路
int source,dest,minLen,minCost,minSpend;
const int MAX=1010,INF=2000000010;
int r[MAX][MAX],s[MAX][MAX],dist[MAX],spend[MAX];//r--长度 s--花费 dist--单源路径
bool mark[MAX];
void dij(int p) //p表示源点 -----先标记源点,再依次选择、标记、更新n-1轮。
{
int i,j,minTmp,now;
for(i=1;i<=n;i++){ //包含等号
dist[i]=r[p][i];
spend[i]=s[p][i];
}
mark[p]=1;
for(i=0;i<n-1;i++){
minTmp=INF;minSpend=INF;
for(j=1;j<=n;j++){
if(mark[j]==0&&dist[j]<minTmp){
minTmp=dist[j];
minSpend=spend[j];
now=j;
}
if(mark[j]==0&&dist[j]==minTmp){
if(spend[j]<minSpend){
minSpend=spend[j];
now=j;
}
}
}
mark[now]=1;
for(j=1;j<=n;j++){ //i用在外层循环,在内层循环再用岂能不错!!检查好久才发现。。。
if(mark[j]==0&&dist[now]+r[now][j]<dist[j]){
dist[j]=dist[now]+r[now][j];
spend[j]=spend[now]+s[now][j];
}
if(mark[j]==0&&dist[now]+r[now][j]==dist[j]){
if(spend[now]+s[now][j]<spend[j])
spend[j]=spend[now]+s[now][j];
}
}
}
minLen=dist[dest];
minCost=spend[dest];
}
int main()
{
int i,j;
int a,b,c,d;
//freopen("G:\\in.txt", "r", stdin);
while(scanf("%d%d",&n,&m)!=EOF){
if(n==0&&m==0) break;
for(i=0;i<=n;i++){ //---------------邻接矩阵初始化-----------
for(j=0;j<=n;j++)
r[i][j]=INF; //初始化各边
r[i][i]=0;
mark[i]=0; //标记
dist[i]=INF; //单源路径
}
for(i=0;i<m;i++){
scanf("%d%d%d%d",&a,&b,&c,&d);
if(c<r[a][b]){
r[a][b]=r[b][a]=c; //双边啊。。。。。。。。。。。。。。。
s[a][b]=s[b][a]=d;
}
if(c==r[a][b]){
if(d<s[a][b])
s[a][b]=s[b][a]=d;
}
}
scanf("%d%d",&source,&dest);
minLen=INF; minCost=INF; //迪克斯特拉算法前要初始化源点到终点的最小距离。
dij(source);
printf("%d %d\n",minLen,minCost);
}
return 0;
}
法2:dij+深搜。先用dij求单源路径,再用dfs求花费。
/*
图的邻接矩阵存储+dij+深搜(3各要素初始化:边+标记+单源路径) 从存储就解决重边问题。
*/
#include <stdio.h>
int n,m; //n各城市,m条路
int source,dest,minLen,minCost;
const int MAX=1010,INF=2000000010; //INF至少设置千万数量级才可以AC。。。。
int r[MAX][MAX],s[MAX][MAX],dist[MAX];//r--长度 s--花费 dist--单源路径
bool mark[MAX];
void dij(int p) //p表示源点 -----先标记源点,再依次选择、标记、更新n-1轮。
{
int i,j,minTmp,now;
for(i=1;i<=n;i++){ //包含等号
dist[i]=r[p][i];
// printf("***%d***\n",dist[i]);
}
mark[p]=1;
for(i=0;i<n-1;i++){
minTmp=INF;
for(j=1;j<=n;j++){
if(mark[j]==0&&dist[j]<minTmp){
minTmp=dist[j];
now=j;
}
}
mark[now]=1;
for(j=1;j<=n;j++){ //i用在外层循环,在内层循环再用岂能不错!!检查好久才发现。。。
if(mark[j]==0&&dist[now]+r[now][j]<dist[j])
dist[j]=dist[now]+r[now][j];
}
}
minLen=dist[dest];
// printf("---%d %d:%d---\n",source,dest,minLen);
}
void dfs(int nowP,int len,int cost) //包含当前结点nowP在内的长度和花费,需要先赋值,本轮再跳出。
{
int i;
if(nowP==dest){
if(len==minLen){
if(cost<minCost)
minCost=cost;
}
return;
}
if(len>minLen)
return;
for(i=1;i<=n;i++){
if(mark[i]==0&&r[nowP][i]<INF){
mark[i]=1;
dfs(i,len+r[nowP][i],cost+s[nowP][i]);
mark[i]=0;
}
}
}
int main()
{
int i,j;
int a,b,c,d;
// freopen("G:\\in.txt", "r", stdin);
while(scanf("%d%d",&n,&m)!=EOF){
if(n==0&&m==0) break;
//---------------邻接矩阵初始化-----------
for(i=0;i<=n;i++){
for(j=0;j<=n;j++)
r[i][j]=INF; //初始化各边
r[i][i]=0;
mark[i]=0; //标记
dist[i]=INF; //单源路径
}
//----------------------------------
for(i=0;i<m;i++){
scanf("%d%d%d%d",&a,&b,&c,&d);
if(c<r[a][b]){
r[a][b]=r[b][a]=c; //双边啊。。。。。。。。。。。。。。。
s[a][b]=s[b][a]=d;
}
if(c==r[a][b]){
if(d<s[a][b])
s[a][b]=s[b][a]=d;
}
// printf("******%d*********\n",r[a][b]);
}
scanf("%d%d",&source,&dest);
minLen=INF; //迪克斯特拉算法前要初始化源点到终点的最小距离。
dij(source);
for(j=0;j<=n;j++) //dij算法后部分已被标记,重新初始化标记
mark[j]=0;
minCost=INF; //初始化最小花费
mark[source]=1;
dfs(source,0,0); //包括初始结点在内的最短路径 和 最小花费
printf("%d %d\n",minLen,minCost);
}
return 0;
}