SPFA找单源最短路(skiing)(bfs和dfs)

 http://poj.org/problem?id=3037

贝西和农场主约翰的其他奶牛今年冬天要去滑雪。有一天,贝茜发现自己在R(1<=R<=100)的左上角被E(-25<=E<=25)高地的C(1<=C<=100)网格所占据。为了与FJ和其他奶牛一起参加一个奶牛聚会,她必须尽可能快地走到右下角,只走北、南、东和西。

贝西以最初的速度V(1<=V<=1,000,000)开始旅行。她发现她的速度和她的仰角变化之间存在着显著的关系。当贝西从高度A的位置移动到相邻的8B位置时,她的速度乘以2^(A-B)。贝茜从一个地点到邻近地点旅行所需的时间是她在第一个地点时速度的倒数。

找出两个最小的时间,贝茜将需要加入她的奶牛朋友。

输入

*第1行:三个空格分隔的整数:v、R和C,它们分别表示Bessie的初始速度以及网格中的行数和列数。

*第2行.R+1:C整数,表示网格上相应位置的仰角E。

输出量

一个数字值,打印成两个精确的小数位:Bessie到达网格右下角所需的最小时间。

样本输入

1 3 3
1 5 3
6 3 5
2 4 3

样本输出

29.00

 bfs版:(不超时)

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cmath>
#include<string>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
#define INF 1.7e+300//double的最大值为1.7e+308 
//使用邻接表 
struct node {
	ll id;//节点 
	double d;//权值 
	ll next_s;//指向 
}side[50000];

ll head[10001],cn;
//初始化头结点和cnt(这里用的cn,因后面已有cnt) 
void init(){
	memset(head,-1,sizeof(head));
	cn=0;
}
//使x指向y,并赋予权值 
void add(ll x,ll y,double z){
	side[cn].id=y;
	side[cn].d=z;
	side[cn].next_s=head[x];
	head[x]=cn++;
}
double dis[10001];
ll flag[10001]; 
double h[101][101];//此题每个点的高度 
double V[101][101];//每个点的速度 
int main(){
	ll v,r,c;
	cin>>v>>r>>c;
	for(int i=1;i<=r;i++){
		for(int j=1;j<=c;j++){
			 cin>>h[i][j];
		}
	}
	
	ll cnt=0;
	V[1][1]=v*1.0;
	//第一个点的速度确定,其他点的速度就确定了,根据底数相同,指数相加 
	for(int i=1;i<=r;i++){
		for(int j=1;j<=c;j++){
			if(h[1][1]>h[i][j])
			V[i][j]=V[1][1]*pow(2,h[1][1]-h[i][j]);
			else V[i][j]=V[1][1]/pow(2,h[i][j]-h[1][1]);
		}
	}
	init(); 
	for(int i=1;i<=r;i++){
		for(int j=1;j<=c;j++){
			cnt++;
			if(j+1<=c){
				//因速度不同,两个点相互到达的权值不同 
				add(cnt,cnt+1,1.0/V[i][j]);
				add(cnt+1,cnt,1.0/V[i][j+1]);
			}
			if(i+1<=r){
				add(cnt,cnt+c,1.0/V[i][j]);
				add(cnt+c,cnt,1.0/V[i+1][j]);
			}
		}
	}
	//初始化dis数组 
	for(int i=1;i<=cnt;i++){
		dis[i]=INF;
	}
	dis[1]=0;
	//接下来SPFA求单源最短路(可解决负权边,无向与有向都适用) 
	flag[1]=1;
	queue<ll> p;
	//先将源点压入,使用SPFA不需要对dis[1][i]进行初始化 
	p.push(1);
	while(!p.empty()){
		ll index=p.front();
		p.pop();
		flag[index]=0;//出队列取消标记 
		for(int i=head[index];i!=-1;i=side[i].next_s){//遍历index所能到达的点side[i].id; 
			if(dis[side[i].id]>dis[index]+side[i].d){//如果满足就松弛 
				dis[side[i].id]=dis[index]+side[i].d;
					if(!flag[side[i].id]){//如果没有入队,就入队 
				p.push(side[i].id);
				flag[side[i].id]=1;
			}//注意,两个if是嵌套,不是并列 
			}
		}
	}

	printf("%.2f\n",dis[cnt]);//double类型输入必须用%lf,输出最好用%f,精度高 
	return 0;
}

dfs版:(超时)

 

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cmath>
#include<string>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
#define INF 1.7e+300//double的最大值为1.7e+308 
//使用邻接表 
struct node {
	ll id;//节点 
	double d;//权值 
	ll next_s;//指向 
}side[50000];
 
ll head[10001],cn;
//初始化头结点和cnt(这里用的cn,因后面已有cnt) 
void init(){
	memset(head,-1,sizeof(head));
	cn=0;
}
//使x指向y,并赋予权值 
void add(ll x,ll y,double z){
	side[cn].id=y;
	side[cn].d=z;
	side[cn].next_s=head[x];
	head[x]=cn++;
}
double dis[10001];
ll flag[10001]; 
double h[101][101];//此题每个点的高度 
double V[101][101];//每个点的速度 

void dfs_spfa(ll x){
	flag[x]=1;
	for(int i=head[x];i!=-1;i=side[i].next_s){
		if(dis[side[i].id]>dis[x]+side[i].d){
			dis[side[i].id]=dis[x]+side[i].d;
			if(!flag[side[i].id]){
				dfs_spfa(side[i].id);
			}
		}
	}
	flag[x]=0;
} 

int main(){
	ll v,r,c;
	cin>>v>>r>>c;
	for(int i=1;i<=r;i++){
		for(int j=1;j<=c;j++){
			 cin>>h[i][j];
		}
	}
	
	ll cnt=0;
	V[1][1]=v*1.0;
	//第一个点的速度确定,其他点的速度就确定了,根据底数相同,指数相加 
	for(int i=1;i<=r;i++){
		for(int j=1;j<=c;j++){
			if(h[1][1]>h[i][j])
			V[i][j]=V[1][1]*pow(2,h[1][1]-h[i][j]);
			else V[i][j]=V[1][1]/pow(2,h[i][j]-h[1][1]);
		}
	}
	init(); 
	for(int i=1;i<=r;i++){
		for(int j=1;j<=c;j++){
			cnt++;
			if(j+1<=c){
				//因速度不同,两个点相互到达的权值不同 
				add(cnt,cnt+1,1.0/V[i][j]);
				add(cnt+1,cnt,1.0/V[i][j+1]);
			}
			if(i+1<=r){
				add(cnt,cnt+c,1.0/V[i][j]);
				add(cnt+c,cnt,1.0/V[i+1][j]);
			}
		}
	}
	//初始化dis数组 
	for(int i=1;i<=cnt;i++){
		dis[i]=INF;
	}
	dis[1]=0;
	dfs_spfa(1);
	printf("%.2f\n",dis[cnt]);//double类型输入必须用%lf,输出最好用%f,精度高 
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值