浙大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;
}