题目样例(2020华为C++笔试原题)
N个城市,编号1到N。城市间有R条单向道路。
每条道路连接两个城市,有长度和过路费两个属性。
Bob只有K块钱,他想从城市1走到城市N。问最短共需要走多长的路。如果到不了N,输出-1
2<=N<=100
0<=K<=10000
1<=R<=10000
每条路的长度 L, 1 <= L <= 100
每条路的过路费T , 0 <= T <= 100
输入
输入的第一行包含整数K,0 <= K <= 10000,Bob可以在途中花费的最大硬币数。
第二行包含整数N,2 <= N <= 100,即城市总数。
第三行包含整数R,1 <= R <= 10000,道路总数。
以下R行中的每一行通过指定由单个空白字符分隔的整数S,D,L和T来描述一条道路:
S是源城市,1 <= S <= N.
D是目的地城市,1 <= D <= N.
L是道路长度,1 <= L <= 100
T是收费(以硬币数表示),0 <= T <= 100
请注意,不同的道路可能具有相同的源和目的地城市。
输出
输出的第一行和唯一一行应包含从城市1到城市N的最短路径的总长度,其总收费小于或等于K个硬币。
如果此路径不存在,则只应将数字-1写入输出。
输出样例
5
6
7
1 2 2 3
2 4 3 3
3 4 2 4
1 3 4 1
4 6 2 1
3 5 2 0
5 4 3 2
输出样例
11
思路
DFS+剪枝函数(里面有回溯思想)
代码
#include<iostream>
#include<vector>
#include<cstring> //c+头文件cstring c语言是string.h 都包含memset函数
#include <algorithm> //包含min函数
using namespace std;
int K,N,R;
struct Road
{
int d,L,t; //终点为d,长度为L,过路费为t
};
vector<vector<Road> > cityMap(110); //邻接表 vector元素个数为110即行数为110,即源路径-》目的路径所有个数之和
int minLen = 1<<30; //目前的最优路径
int totalLen; //正在走的路径的长度
int totalCost; //正在走的路径的花销
int visited[110];
int minL[110][10100]; //minL[i][j]表示从1到i点(i城市),花销为j的最短路的长度
void Dfs(int s)
{
if(s == N) //边界条件 ,因为从城市1开始,如果源城市等于城市数,说明到了最后一个,结束了
{
minLen = min(minLen,totalLen); //这里最终是出口,寻找到了最后一个城市,最后才返回了minLen
return;
}
for(int i=0;i<cityMap[s].size();++i)
{
//逐个考察s右路连到的点
int d=cityMap[s][i].d;
if(!visited[d]) //如果目的城市没有访问过
{
//正在走的路径长度加上当前的赋值给cost
int cost=totalCost+cityMap[s][i].t;
//这里cost有两个作用,一个判断,一个比较
if(cost>K) continue;
if(totalLen+cityMap[s][i].L>=minLen||
totalLen+cityMap[s][i].L>=minL[d][cost]) continue;
//这里进行追加,从源城市1开始出发,追加给正在走的路径长度,路径花费
totalLen+=cityMap[s][i].L;
totalCost+=cityMap[s][i].t;
//这一步很关键,把正在走的路径长度赋值给二维数组存储下来
minL[d][cost]=totalLen;
visited[d]=1;
Dfs(d);
//调用核心思想还有回溯,如果不满足条件,还要将状态还原,调用一层一层进入,一层一层返回
visited[d]=0;
totalCost-=cityMap[s][i].t;
totalLen-=cityMap[s][i].L;
}
}
}
int main()
{
cin>>K>>N>>R;
for(int i=0;i<R;i++)
{
int s;
Road r;
cin>>s>>r.d>>r.L>>r.t;
if(s!=r.d) //源城市不能等于目的城市
cityMap[s].push_back(r);//关键这里将结构体存储下来,相当于存储源城市到目的城市一个点
}
//不要忘记了初始化最短路径数组和访问数组
for(int i = 0;i<110;i++)//外层表示道路总数
{
for(int j=0;j<10100;j++)
{
minL[i][j]=1<<30;
}
}
//数组初始化为0,如果=0,存在移植性问题,有的编译器不支持,=0和memset差不多性能,但是memset内部有优化,推荐memset
memset(visited,0,sizeof(visited));//memset:作用是在一段内存块中填充某个给定的值,它是对较制大的结构体或数组进行清零操作的一种zhidao最快方法。
totalLen = 0;
totalCost = 0;
visited[1]=1;
//1<<30左移30位,相当于2的30次方,定义一个很大的数
minLen=1<<30;
Dfs(1);
if(minLen<1<<30)
cout<<minLen<<endl;
else
{
cout<<"-1"<<endl;
}
return 0;
}