HDU 1142 A Walk Through the Forest(详细过程图+注释,dijkstra+记忆化搜索)

题目链接

A Walk Through the Forest

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 11248 Accepted Submission(s): 4119

Problem Description

Jimmy experiences a lot of stress at work these days, especially since his accident made working difficult. To relax after a hard day, he likes to walk home. To make things even nicer, his office is on one side of a forest, and his house is on the other. A nice walk through the forest, seeing the birds and chipmunks is quite enjoyable.
The forest is beautiful, and Jimmy wants to take a different route everyday. He also wants to get home before dark, so he always takes a path to make progress towards his house. He considers taking a path from A to B to be progress if there exists a route from B to his home that is shorter than any possible route from A. Calculate how many different routes through the forest Jimmy might take.

Input

Input contains several test cases followed by a line containing 0. Jimmy has numbered each intersection or joining of paths starting with 1. His office is numbered 1, and his house is numbered 2. The first line of each test case gives the number of intersections N, 1 < N ≤ 1000, and the number of paths M. The following M lines each contain a pair of intersections a b and an integer distance 1 ≤ d ≤ 1000000 indicating a path of length d between intersection a and a different intersection b. Jimmy may walk a path any direction he chooses. There is at most one path between any pair of intersections.

Output

For each test case, output a single integer indicating the number of different routes through the forest. You may assume that this number does not exceed 2147483647

Sample Input

5 6
1 3 2
1 4 2
3 4 3
1 5 12
4 2 34
5 2 24
7 8
1 3 1
1 4 1
3 7 1
7 4 1
7 5 1
6 7 1
5 2 1
6 2 1
0

Sample Output

2
4

题目大意

给你一个图,找最短路。但是有个非一般的的条件:如果a,b之间有路,且你选择要走这条路,那么必须保证a到终点的所有路都小于b到终点的一条路。问满足这样的路径条数 有多少;
(吐槽一下,不知道是这题目表达的太蹩脚,还是我的英语太蹩脚??真的人都傻了,看了好多人的说法才敢确定题意,没清楚题意时我用的是普通dfs找有多A->B这种路径。。。。。就是起点A,终点B。。。。。。。。我好苦啊)

思路

1.1为起点,2为终点,因为要走A->B路时,必须保证那个条件,所以从终点开始使用单源最短路Dijkstra算法,就得到了最短的一条路,作为找路的最低限度。

2.然后记忆化搜索dfs每条路,看看满足题意的路径有多少条。当然,这个需要从起点开始搜,因为d[i]数组中保存的都是该点到终点的最短距离。

3.dp[i]表示i点一共可以有多少条路。这样搜索之后,dp[1]就是从起点到终点所有满足题意的路径的条数。

本题记忆化搜索过程图(Dijkstra的算法教程网上实在太多了)
int dfs(int u){
    if(dp[u])//记忆化,剪枝,如果此点已经走过,则返回“此点->终点的路径数量”
        return dp[u];
    if(u==2)//到达终点,返回路径数量1条
        return 1;
    int tmp=0;
    for(int i=0;i<adj[u].size();i++){
        int v=adj[u][i].v;
        if(d[v]<d[u])//如果满足题目条件,则继续往下搜索
            tmp+=dfs(v);
    }
    return dp[u]=tmp;//dp[i]表示i点一共可以有多少条路
}

以下图解没有用到记忆化,简单来说就是保存已经走过的节点的dp[],再次访问到就直接返回

总体图
在这里插入图片描述
详细记忆化搜索图,由于是递归算法,应该从2往1看,依次找到符合条件的接节点对,最终找到符合条件的路径
从右往左看是为了方便理解,这里需要理解递归的思想
在这里插入图片描述在这里插入图片描述
下面三条路径只有中间一条不符合条件
在这里插入图片描述

完整代码

#include <iostream>
#include <vector>
#include <algorithm>
#define maxn 1005
#define inf 0x3f3f3f3f

using namespace std;

struct node{
    int v;
    int dis;//到v的长度
    node(int _v,int _dis){
        v=_v;
        dis=_dis;
    }
};

vector<node> adj[maxn];
int n,m,d[maxn];
bool inq[maxn];
int dp[maxn];

void dij(int s){
    memset(inq,false,sizeof(inq));
    fill(d,d+maxn,inf);//初始化为inf不要用memset(只能初始化为0和-1)
    d[s]=0;//源点到自己距离为0
    for(int i=1;i<=n;i++){
        int u=-1,mn=inf;
        for(int j=1;j<=n;j++){
            if(!inq[j]&&d[j]<mn){
                mn=d[j];//最短路径
                u=j;//最短路径的标号
            }
        }
        //cout<<u<<endl;
       // if(u==-1) return;
        inq[u]=true;//标记
        for(int j=0;j<adj[u].size();j++){//遍历adj[u],这里安全点应该用vector<node>::size_type j=0;
            int v=adj[u][j].v;
            int dis=adj[u][j].dis;
            if(!inq[v]&&d[u]+dis<d[v]){//松弛
                d[v]=d[u]+dis;
            }
        }
    }
}

int dfs(int u){
    if(dp[u])//记忆化,剪枝,如果此点已经走过,则返回“此点->终点的路径数量”
        return dp[u];
    if(u==2)//到达终点,返回路径数量1条
        return 1;
    int tmp=0;
    for(int i=0;i<adj[u].size();i++){
        int v=adj[u][i].v;
        if(d[v]<d[u])//如果满足题目条件,则继续往下搜索
            tmp+=dfs(v);
    }
    return dp[u]=tmp;//dp[i]表示i点一共可以有多少条路
}

int main()
{
    while(cin>>n){
        if(!n)
            break;
        //初始化adj图
        for(int i=1;i<=n;i++)
            adj[i].clear();
        cin>>m;
        int a,b,c;
        while(m--){
            cin>>a>>b>>c;
            //无向图
            adj[a].push_back(node(b,c));
            adj[b].push_back(node(a,c));
        }
        dij(2);
        /*for(int i=1;i<=n;i++){  //输出最短路径长度
            cout<<i<<"->2:"<<d[i]<<endl;
        }*/
        memset(dp,0,sizeof(dp));
        cout<<dfs(1)<<endl;
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值