九度OJ:题目1406 上班啦

题目描述:

淘小宝最近进入了杭州淘宝实习了,可是他所住的地方离工作地点很远(为了省钱)。虽说杭州是个美丽的旅游城市,可是其公共交通却十分被人诟病,早高峰的时间,汽车跟爬的一样,所以经过一个星期的折腾之后,淘小宝决定骑车前往公司上班。

         为了每天尽可能的节约体力,同时更多地领略杭州城市的美丽风光,淘小宝就想请你告诉他,从他的住所出发,最短的骑车距离是多少?同时,也请你告诉他,在骑车路径最短的前提下,他有多少种不同的选择?

         我们已知淘小宝将整个城市交通按照各个路口以及路口间的道路,抽象成一幅由N 个点与M 条边组成的地图,同时也告知你这些边之间距离,请你告诉淘小宝,最短的骑车距离是多少以及这样长度的不同路径条数。若路径不存在,则按样例输出一行两个-1。

输入:

每个测试案例包含两部分。

第一行输入包括两个整数N和M , 其中N代表地图上不同的路口数量,且2 <= N <= 500。M为总的路径条数,我们保证输入数据中,任意两个点之间的路径不会超过一条,且M一定是非负整数。

第二行开始会输出M行,每行3个正整数,由空格隔开。分别表示这条路径的两个端点E1 和E2,以及这条路径的长度L,其中1 <= L <= 10000。

这里需要注意的是,地图上所有路口是从1开始编号的,其中淘小宝的住处一定被编为1号,而淘宝公司则被编为N号。

输出:

对于每个测试案例,输出一行,每行包含两个整数,用空格分开。若答案存在,则输出最短路径的长度以及方案数;若不存在,则输出-1 -1。



样例输入:
4 4
1 2 1
4 2 1
1 3 1
3 4 
3 1
1 2 1

样例输出:

        2 2

        -1 -1


[解题思路]

1.大致看一下题目就会发现这是图论里面的求两点最短路径的问题,这时候自然就会想到要用Dijkstra算法

[题目难点]

方案数目的求解(可能会少算)

[解题步骤]

1.构造无向图,代码如下

		for(i=1;i<=N;i++)
		{
			for(j=1;j<=N;j++)
				map[i][j]=map[j][i]=MAX;
			dist[i]=MAX;
		}
		for(i=1;i<=M;i++){
			scanf("%d %d %d",&s,&t,&w);
			if(map[s][t]>w){
				map[s][t]=map[t][s]=w;
			}
		}

2.Dijkstra算法的应用

void Dijkstra(){
	int i,j;
	memset(visited,false,sizeof(visited));
	for(i=1;i<=N;i++){
		dist[i]=map[1][i];
		if(dist[i]!=MAX){
			insert(i);
			visited[i]=true;
		}
	}
	dist[1]=0;
	while(l!=NULL){
		int m=getMin();
		for(j=1;j<=N;j++){
			if(map[m][j]!=MAX){
				if(dist[j]>dist[m]+map[m][j]) dist[j]=dist[m]+map[m][j];
				if(visited[j]==false) insert(j);
				visited[j]=true;
			}
		}
	}
}
3.求出一共的方案数

		if(dist[N]==MAX){
			printf("-1 -1\n");
		}
		else{
			countNum(dist[N],N);
			printf("%d %d\n",dist[N],count);
		}
其中countNum();函数实现的代码如下

void countNum(int num,int s){
    for(int i=1;i<=N;i++){
        if(num-map[s][i]==dist[i]){
            if(i==1) count++;
            else countNum(dist[i],i);
        }
    }
}
这时候原先我的错误代码是

			for(int i=1;i<=N;i++){
				if(num-map[s][i]==dist[i]){
					count++;
				}
			}
就是由于少考虑了重复的路径

比如a->b->c为花费最少额路径,错误代码就只有加一次,但是如果a->d->...->b的花费与a->b的一样呢,这样就少考虑的很多的路径,如图:

九度OJ:题目1406 上班啦_第1张图片

到这里程序的基本思路就理清楚了,对应程序的源代码如下:

#include<stdio.h>
#include<string.h>
#include<malloc.h>
#define R 1001
#define L 1001
#define MAX 99999

typedef struct Node* link;
struct Node{
	int e;//存储顶点
	link next;
};

link l=NULL;
int N,M;
int map[R][L];
int dist[R];
int count;
bool visited[R]={0};

void insert(int x){
	if(l==NULL){
		link p=(link)malloc(sizeof(Node));
		p->e=x;
		l=p;
		l->next=NULL;
	}
	else{
		link p=(link)malloc(sizeof(Node));
		p->e=x;
		p->next=l->next;
		l->next=p;
	}
	return;
}

int getMin(){
	if(l->next==NULL){
		int x=l->e;
		l=NULL;
		return x;
	}
	else{
		link p=l;
		link r=l;
		link q=l->next;
		link t=l;
		while(q!=NULL){
			if(dist[q->e]<dist[p->e]){
				t=r;
				p=q;
			}
			r=q;	
			q=q->next;
		}
		int x=p->e;
		if(p==l){
			l=l->next;
			free(p);
		}
		else{
			t->next=p->next;
			free(p);
		}
		return x;
	}
}
void Dijkstra(){
	int i,j;
	memset(visited,false,sizeof(visited));
	for(i=1;i<=N;i++){
		dist[i]=map[1][i];
		if(dist[i]!=MAX){
			insert(i);
			visited[i]=true;
		}
	}
	dist[1]=0;
	while(l!=NULL){
		int m=getMin();
		for(j=1;j<=N;j++){
			if(map[m][j]!=MAX){
				if(dist[j]>dist[m]+map[m][j]) dist[j]=dist[m]+map[m][j];
				if(visited[j]==false) insert(j);
				visited[j]=true;
			}
		}
	}
}
void countNum(int num,int s){
    for(int i=1;i<=N;i++){
        if(num-map[s][i]==dist[i]){
            if(i==1) count++;
            else countNum(dist[i],i);
        }
    }
}
int main(){
	int i,j;
	int s,t,w;
	while(scanf("%d %d",&N,&M)==2){
		for(i=1;i<=N;i++)
		{
			for(j=1;j<=N;j++)
				map[i][j]=map[j][i]=MAX;
			dist[i]=MAX;
		}
		for(i=1;i<=M;i++){
			scanf("%d %d %d",&s,&t,&w);
			if(map[s][t]>w){
				map[s][t]=map[t][s]=w;
			}
		}
		Dijkstra();
		count=0;
		if(dist[N]==MAX){
			printf("-1 -1\n");
		}
		else{
			countNum(dist[N],N);
			printf("%d %d\n",dist[N],count);
		}
		free(l);
		l=NULL;
	}
	return 0;
}
题目地址

http://ac.jobdu.com/problem.php?pid=1406



你可能感兴趣的:(数据结构,算法,九度OJ)