( 回溯 )uva 301 - Transportation

做过了几道简单的回溯题目,慢慢发现其实所谓的回溯算法,实际上是一种思想,一种将具体问题分为若干步骤,递归遍历解答树的思想。

本就是枚举,各种方式的枚举,方式不同,但目的相同,都是想要遍历到所有合法的条件

在一层层的遍历过程中,加入对目前选择的合法性的判断——从而在此分支上不再向深处遍历,以达到节省时间的目的。这是回溯的关键思想。

而回溯的关键步骤之一,是对原状态的恢复。

单层的dfs是不需要回复原状态的,因为单纯的dfs只需要在解答树的每一个节点上走一次,一遍就可以得到所需要的答案,节点内容改变与否对答案没有影响。

而对于回溯来说,它需要在一条路线上向深处延伸,它也需要在深处再沿这条路线回到上面的节点处,以开始新的不同的dfs。

所以回溯时,会对同一解答树的同一节点有多次访问,我们要保证每一次访问时,该节点的值都应该是原始读入的值,而不是上一次遍历过后被修改过的值。

所以回溯的关键步骤之一,就是在“往回倒”的时候,将节点的原始状态恢复。


即使对回溯算法有了比较深入的了解,当遇见具体的情况时,也不一定就能就能设计好合适的算法,你需要掌握回溯的多种代码实现方法,需要对问题的情形有清楚的分析,等等


但是最根本的,是你脑海中对于回溯的理解是不是正确。

现在对于这道题目,我脑海中的回溯,就是为了实现枚举的递归:(我觉得这是一种思路,不是一种具体的算法)


我觉得这道题目其实也挺好,隐隐感觉题目把难度给降了,并不太符合实际情况。。不过话说回来不降难度我也就更做不出来了……


问题简单描述:有一辆车从A城市开往B城市,途中有m个站,车上最多的载客人数为n人,每一个站的价格就是终点和起点的差值,现在有k分订单,要求找到这辆车的最大利润(在每一个站要么就将订单上的人全载走,要么不载)

#include<stdio.h>
#include<string.h>
#define MAXN

int n,m,od;
int a[30],b[30],c[30];
int sum[8];
int max_t,max_;

void dfs(int cur)
{
    if(cur==od)
    {
        max_t=0;
        for(int i=0;i<m;i++)
            max_t+=sum[i];
        if(max_t>max_)
            max_=max_t;
        return;
    }
    int flag=1;
    for(int i=a[cur];i<b[cur];i++)
    {
        if(sum[i]+c[cur]>n) flag=0;
    }
    if(flag)
    {
        for(int i=a[cur];i<b[cur];i++)
            sum[i]+=c[cur];
        dfs(cur+1);
        for(int i=a[cur];i<b[cur];i++)
            sum[i]-=c[cur];
    }
    dfs(cur+1);/*我之前一直迷惑这里的dfs如何来实现对订单取还是不取的枚举呢?后来想到了,遍历这一个点时,不把这个点的人数往总人数上面加,即使遍历过,也相当于没有遍历,既没有去取这个订单。 取过之后再回溯回来,又回归了原始状态,然后接下来再枚举的就是此点不取的情况了。可以比较两处向下递归的区别:一处伴随 for(int i=a[cur];i<b[cur];i++)  sum[i]+=c[cur],而下面的递归没有这一行代码~*/
}

int main()
{
    while(scanf("%d%d%d",&n,&m,&od)!=EOF)
    {
        if(n==0&&m==0&&od==0)
            break;
        int a1,a2,a3;
        for(int i=0;i<od;i++)
        {
            scanf("%d%d%d",&a1,&a2,&a3);
            a[i]=a1;b[i]=a2;c[i]=a3;
        }
        max_=0;
        dfs(0);
        printf("%d\n",max_);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值