北京大学OpenJuge 1724:ROADS(寻路问题)(DFS+剪枝)

1724:ROADS

总时间限制: 

1000ms

内存限制: 

65536kB

描述

N cities named with numbers 1 ... N are connected with one-way roads. Each road has two parameters associated with it : the road length and the toll that needs to be paid for the road (expressed in the number of coins). 
Bob and Alice used to live in the city 1. After noticing that Alice was cheating in the card game they liked to play, Bob broke up with her and decided to move away - to the city N. He wants to get there as quickly as possible, but he is short on cash. 

We want to help Bob to find the shortest path from the city 1 to the city N that he can afford with the amount of money he has. 

输入

The first line of the input contains the integer K, 0 <= K <= 10000, maximum number of coins that Bob can spend on his way. 
The second line contains the integer N, 2 <= N <= 100, the total number of cities. 

The third line contains the integer R, 1 <= R <= 10000, the total number of roads. 

Each of the following R lines describes one road by specifying integers S, D, L and T separated by single blank characters : 

  • S is the source city, 1 <= S <= N 
  • D is the destination city, 1 <= D <= N 
  • L is the road length, 1 <= L <= 100 
  • T is the toll (expressed in the number of coins), 0 <= T <=100


Notice that different roads may have the same source and destination cities.

输出

The first and the only line of the output should contain the total length of the shortest path from the city 1 to the city N whose total toll is less than or equal K coins. 
If such path does not exist, only number -1 should be written to the output. 

样例输入
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

来源

CEOI 1998

附上中文题面:

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
N
R
s 1 e 1 L 1 T 1
s 1 e 2 L 2 T 2
...
s R e R L R T R
s e是路起点和终点

思路:这道题其实描述的情景挺简单的,我们用K N R分别表示手里钱、城市的个数、单向道路的个数

            又用结构体把每条路的终点、距离、花费都存起来

            当起点不等于终点的时候,我们就把每条路的四个信息(起点自己定义)都存进vector容器G中。

            开始进入正题:我们要通过dfs算出最短的距离:

            如果说已经走到终点我们就更新最小距离

            没有走到终点我们就需要进行遍历

            这当中的路径具有不确定性,不经过某一个点r.d和经过r.d这两种情况都要分类讨论。

            遍历完成之后回到主函数:找到最短路径就输出,找不到就输出-1

            如果你觉得这样就能AC的话,你就有点naive了。

            因为这样找到的路线复杂度非常大显然会超时,我们还要进行剪枝:

            如果说走到某一个城市花费的钱已经超出预算,我们就不再操作;如果说走到某一个城市走出的距离已经超过了之前走到这个城市的最小距离,那么我们认为这个策略是错误的,仍然跳出;用minL[k][m]表示走到某个城市k总过路费为m的条件下,最优路径的长度,如果在后续的搜索中再次走到k过路费恰好为m,而路径长度已经超过之前存在minL中的数据,依然要剪枝。

            在这个寻路问题当中,剪枝问题非常重要。而且需要学会将剪枝的思想熟练地应用到题目中,以达到题目的时间要求,加油ACMer!

源代码:

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
int K,N,R;
struct Road{
    int e,l,t;
};
vector <vector <Road> >G(110);
int minLen=1<<30;
int totalLen;
int totalCost;
int visited[110];
int minL[110][10100];
void dfs(int s){
    if(s==N){//是否已经走到终点
        minLen=min(minLen,totalLen);//如果小的话就要更新minLen
    return ;
    }
    for(int i=0;i<G[s].size();++i){//遍历
        Road r = G[s][i];
        //最优性剪枝1
        if(totalCost+r.t>K)
            continue;
        if(!visited[r.e]){//如果没有访问过r.d
            //最优性剪枝2
            if(totalLen+r.l>=minLen)
                continue;
            if(totalLen+r.l>=minL[r.e][totalCost+r.t])
                continue;
            //如果剪枝不成立
            minL[r.e][totalCost+r.t]=totalLen+r.l;
            totalLen+=r.l;
            totalCost+=r.t;
            visited[r.e]=1;
            dfs(r.e);
            //不走r.d的情况
            visited[r.e]=0;
            totalLen-=r.l;
            totalCost-=r.t;
        }
    }
}
int main(){
    cin>> K >> N >> R;
    for(int i=0;i<R;i++){
        int s;//设置起点
        Road r;
        cin>>s>>r.e>>r.l>>r.t;
        if(s!=r.e)//如果起点不等于终点
        {
            G[s].push_back(r);
        }
    }
    for(int i=0;i<110;i++)
        for(int j=0;j<10100;j++)
        minL[i][j]=1<<30;
    memset(visited,0,sizeof(visited));
    totalLen=0;
    totalCost=0;
    visited[1]=1;
    minLen=1<<30;
    dfs(1);
    if(minLen <(1<<30))
        cout<<minLen<<endl;
    else
        cout<<"-1"<<endl;
}

猜你喜欢

转载自blog.csdn.net/qq_37618760/article/details/81747668