浙大PAT满分之路-(第三天+第四天)甲级1003 Emergency (25 分)[DFS深度优先+最短路径]

浙大PAT满分之路-(第三天)1003


原题:
原题
第一行分别定义了
城市数量N、路(街道)的数量M、我们需要从哪个城市去哪个城市(C1\C2∈[0,N-1])
第二行定义
N个城市中各包含了多少救援队人数
最后的M行定义了
哪两个城市是有街道的(即被连通的)以及路的长度
比如数据为:0 1 5
即表示从0号城市到1号城市是连通的,路的长度为5
在此背景下本题需要我们找到路径最短(优先级最高),并且在路径最短的情况下找到能够呼唤最多二五仔们的情况
最终输出最短路径长度之和以及二五仔的数量
PS:每经过一个城市就可以带走那个城市里的二五仔
苦思良久,看了一下DFS(深度优先)的定义,没有看源代码(打算自己先写个试试)
结果:给出的例题结果正确,PTA中部分正确,估计是深层路线或某些复杂情况没有考虑到。
部分正确
因为时间超出计划表了所以先搁置,明天继续
半成品代码:

#include<iostream>
using namespace std;
const int maxn = 501;
int c1,c2,city_num,road_num;
int road_len[maxn][maxn] = {0};
int rescue[maxn];
int max_men=0;
int min_road=10000;
int len_now = 0;
int man_now = 0;
bool pass_city[maxn] = {false};
void dfs(int start){
	//cout<<"进入DFS函数"<<endl;
	if(len_now > min_road){
	//	cout<<"剪枝"<<endl;
		return;
	}//剪枝,不浪费循环
	man_now+=rescue[start];
	pass_city[start] = true; 
	for(int i = 0;i<city_num;i++){
		
		if(road_len[start][i]!=0 & pass_city[i] == false){
		//	cout<<"从"<<start<<"到"<<i<<"城市"<<endl; 
			len_now+=road_len[start][i];
			
		//	cout<<"现在的路径长度"<<len_now<<endl;
		//	cout<<"现在的二五仔"<<man_now<<endl;
			if(i == c2){
				if(len_now<=min_road){
					min_road = len_now;
					man_now+=rescue[c2];
					if(man_now>max_men){
					max_men = man_now;
					}
				}
				
			//	cout<<"找到一次终点"<<endl;
				//pass_city[i] == false;
				len_now = len_now-road_len[start][i];
				man_now = man_now-rescue[start];
				if(start == c1)continue;
				else return;
			}
			
			dfs(i);
			len_now = len_now-road_len[start][i];
		}
		else if (i == (city_num - 1)){
		//	cout<<"进入死循环,回溯"<<endl;
			len_now = len_now-road_len[start][i];
			man_now = man_now-rescue[start];
			pass_city[start] = false;
			return;
		}
	}
	
	return;
}

int main(){
	int ex1,ex2;
	cin>>city_num>>road_num>>c1>>c2;
	for(int i = 0;i<city_num;i++){
		cin>>rescue[i];
	}
	for(int i = 0;i<road_num;i++){
		cin>>ex1>>ex2;
		cin>>road_len[ex1][ex2];
	}
	dfs(c1);
	cout<<min_road<<" "<<max_men;
	return 0;
}

6.14更新改正代码:
部分错误的原因找到了

一个是最大救援人数的处理上,没有在当找到更短路径时重置最大救援人数,导致最大救援人数可能不是最短路径情况下求得的
二是输出搞错了(英语问题),输出是最短路径的数量–不是最短路径长度
三是把C++的&当成and了,应该是&&
6.14修改N次后决定参考别人的DFS算法
发现我在处理使用递归的时候,对回溯时恢复回溯后的数据上的处理有失误,我最终代码如下

#include<iostream>
using namespace std;
const int maxn = 501;
int c1,c2,city_num,road_num;
int road_len[maxn][maxn] = {0};
int rescue[maxn];
int max_men=0;
int min_road=10000;
int shortest_num = 0;
int len_now = 0;
int man_now = 0;
bool pass_city[maxn] = {false};
void dfs(int start){
	//cout<<"进入DFS函数"<<endl;
	if(len_now > min_road){
	//	cout<<"剪枝"<<endl;
		return;
	}//剪枝,不浪费循环
	man_now+=rescue[start];
	pass_city[start] = true; 
	for(int i = 0;i<city_num;i++){
		
		if(road_len[start][i]!=0 && pass_city[i] == false){
		//	cout<<"从"<<start<<"到"<<i<<"城市"<<endl; 
			len_now+=road_len[start][i];
			
		//	cout<<"现在的路径长度"<<len_now<<endl;
		//	cout<<"现在的二五仔"<<man_now<<endl;
			if(i == c2){
				if(len_now<min_road){
					min_road = len_now;
					shortest_num = 1;
					man_now+=rescue[i];
					max_men = man_now;
					man_now = man_now-rescue[i];
				}
				else if(len_now == min_road){
					man_now+=rescue[i];
					if(man_now>max_men){
					max_men = man_now;  
					}
					shortest_num++;
					man_now = man_now-rescue[i];
				}
			
			//	cout<<"找到一次终点"<<endl;
				//pass_city[i] == false;
				len_now = len_now-road_len[start][i];
				
				//if(start == c1)continue;
				return;
			}
			
			dfs(i);
			len_now = len_now-road_len[start][i];
			man_now = man_now-rescue[i];
		}
		
		else if (i == (city_num - 1)){
		//	cout<<"进入死循环,回溯"<<endl;
			len_now = len_now-road_len[start][i];
			man_now = man_now-rescue[start];
			pass_city[start] = false;
			return;
		}
	}

	return;
}

int main(){
	int ex1,ex2;
	cin>>city_num>>road_num>>c1>>c2;
	for(int i = 0;i<city_num;i++){
		cin>>rescue[i];
	}
	for(int i = 0;i<road_num;i++){
		cin>>ex1>>ex2;
		cin>>road_len[ex1][ex2];
		road_len[ex2][ex1]=road_len[ex1][ex2];
	}
	dfs(c1);
	cout<<shortest_num<<" "<<max_men;
	return 0;
}

参考了大奸猫的代码
感觉我真是把DFS用的稀碎
他比我处理的好的地方在于把数据处理放在函数参数中,这样回溯时就不需要对数据进行处理,递归后的数据变化不影响上个状态的数据,同时我把所有东西放在了一个大循环里,导致我在循环中还需要N多IF条件对循环数据进行筛选引导。
学海无涯,任重而道远。
他的代码(我修改后的和他差不多,基本除了变量名以及一些习惯上的差异就没了,所以直接贴他的代码)

#include<cstdio>
using namespace std;
const int maxn = 510;//图中,所有点的编号范围
const int INF = 1000000000;

/* 关键变量 */ 
int edge[maxn][maxn];//边的长度
int v[maxn];//记录点是否被访问过
int men[maxn];//点的属性:救缓人数 
int s, d;//s出发,到d去
int n,e;//顶点数,边数

/*DFS:从起点出发,遍历所有可能的路径,到达终点*/ 
int shortest_length = INF;  //最短路径的长度
int shortest_num = 0;	//最短路径的数量 (原文中该变量初值为1,我也不知道为什么最短路径初始值他设置为1,为了严谨,考虑到可能会有没有路径能够达到终点的情况,这个变量初值应该为0,虽然OJ中测评没问题,但这样做更严谨)
int max_men = 0;	//最多救援人员数 
void DFS(int cur, int cur_length, int cur_men) {
//cur=当前路径上的终点;cur_length = 当前路径的长度;cur_men = 当前路径的救援人员数 

  /*剪枝*/
  if (cur_length > shortest_length)return; //当前路径长度已经大于最短路径,结束遍历 
  
  /*从起点到终点的路径*/
  if (cur == d) {	//到达终点d
    if (cur_length < shortest_length) {	//当前路径更短 
      shortest_length = cur_length;	//更新最短路径长度 
      shortest_num = 1;	//更新最短路径数目 
      max_men = cur_men;	//更新救援人数 
    }
    else if (cur_length == shortest_length) {//长度同等 
      shortest_num++;	//更新最短路径数目 
      if (cur_men > max_men) {	//救援人数更多,则更新 
        max_men = cur_men;
      }
    }
    return;
  }
  
  /*从cur出发,访问所有可能路径*/
  v[cur] = 1;//cur已经访问过
  for (int i = 0; i < n; i++) {
    if (v[i] == 1 || edge[cur][i] == 0)continue;
    DFS(i, cur_length + edge[cur][i], cur_men + men[i]);
  }
  
  /*从其他结点出发,能够访问cur*/
  v[cur] = 0;
}



int main() {
  
  scanf("%d %d %d %d", &n, &e, &s, &d);
  
  /* 每个城市的可调用的救援人员数*/
  for (int i = 0; i < n; i++) {
    scanf("%d", &men[i]);
  }
  
  /* 每个城市之间的路径长度*/
  for (int i = 0; i < e; i++) {
    int in1, in2, in3;
    scanf("%d %d %d", &in1, &in2, &in3);
    edge[in1][in2] = edge[in2][in1] = in3;
  }
  
  /*遍历各种从起点到终点的路径*/
  DFS(s, 0, men[s]);	//s起点是当前路径的终点,0是当前路径的长度,men[s]是路径的救援人员数 
  
  /*输出最短路径的数目,最短路径上救援人数的最大值*/
  printf("%d %d\n", shortest_num, max_men);
  
  return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值