分道扬镳(DFS+剪枝+邻接表)

题目:

Time Limit: 300ms
Description
编号为1…N 的N个城市之间以单向路连接,每一条道路有两个参数:路的长度和通过这条路需付的费用。

Bob和Alice生活在城市1,但是当Bob发现了Alice玩扑克时欺骗他之后,他决定与她翻脸,离开城市1前往城市N。Bob想尽快到达城市N,但是他的钱不多。

希望你帮助Bob找到一条从城市1到城市N的总费用不超过Bob的承受能力的最短路径

Input
输入的第一行是3个整数 K, N, R ,其中:

K:表示Bob能承受的最大费用,0 ≤ K ≤ 10000
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

Output
为每个测试用例输出一行结果:总费用小于或等于最大费用K的最短路径的长度。如果这样的路径不存在,则仅仅输出 -1 。

Sample Input
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

Sample Output
11

Hint
测试数据的图中可能有重边、有自环。

题解:

显然,可以通过判断当前路径的花费是否已超过K来剪枝,但是这道题只是这样的话还是会得到TLE

解决方法是,可以开一个 minlen[ i ][ fe ] 的二维数组来记录,
表示 到点 i 时花费为 fe 的路径长度为 minlen[ i ][ fe ],
此时 就可以通过判断 当前位于i点且路径花费为fe路径长度为len时
len是否超过minlen[ i ][ fe ] 来剪枝。

#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
const int inf=0x3f3f3f3f;
using namespace std;
struct node
{
    int len,fare,aim;
};
vector<node>G[105];
int minlen[105][10005];
bool book[105];
int K,N,R,S,D,L,T,Road;
void dfs(int i,int step,int money)
{
    if(money>K||step>Road||step>minlen[i][money])
        return;
    if(i==N){
        Road=min(Road,step);
        return;
    }
    else{
        if(step<minlen[i][money])
            for(int e=money;e<=K;e++)
                minlen[i][e]=step;
        for(int j=0;j<G[i].size();j++){
            if(!book[G[i][j].aim]){
                money+=G[i][j].fare;
                ///if(money>K)continue;之前因为多了这两句所以一直WA,想想为什么,为什么剪枝放在这里就错(这里不能去continue!)
                step+=G[i][j].len;
                ///if(step>Road)continue;之前因为多了这两句所以一直WA,想想为什么,为什么剪枝放在这里就错(这里不能去continue!)
                book[G[i][j].aim]=1;
                dfs(G[i][j].aim,step,money);
                money-=G[i][j].fare;
                step-=G[i][j].len;
                book[G[i][j].aim]=0;
            }
        }
    }
    return;
}
int main()
{
    while(~scanf("%d%d%d",&K,&N,&R))
    {
        struct node qq;
        
        /*初始化*/
        memset(book,0,sizeof(book));
        memset(minlen,inf,sizeof(minlen));
        for(int i=1;i<=N;i++)G[i].clear();
        
        /*建邻接表*/
        for(int i=1;i<=R;i++){
            scanf("%d%d%d%d",&S,&D,&L,&T);
            if(S==D)continue;///这一句有没有都对,它会选择最优的路径,所以自环其实是没有影响
            qq.aim=D,qq.fare=T,qq.len=L,G[S].push_back(qq);///用结构体vector建邻接表,可避免重边引起的错误,存储起点对应的终点和路径长度、费用三个信息
        }
        
        Road=inf;///初始化为无穷大
        book[1]=1;
        dfs(1,0,0);
        if(Road==inf) ///路径不存在
            printf("-1\n");
        else
            printf("%d\n",Road);
    }
    return 0;
}

关于剪枝:

剪枝算法按照其判断思路可大致分成两类:可行性剪枝及最优性剪枝。
1 . 可行性剪枝 :该方法判断继续搜索能否得出答案,如果不能直接回溯。
2 . 最优性剪枝:
最优性剪枝,又称为上下界剪枝,是一种重要的搜索剪枝策略。它记录当前得到的最优值,如果当前结点已经无法产生比当前最优解更优的解时,可以提前回溯。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值