poj 1040 Transportation DFS剪枝

恩,网上个个哥们都说这是个水题,惭愧啊,我太水了,只能向大神们学习了。

转自博文:http://www.cnblogs.com/yangliu/archive/2011/12/22/2298497.html

                  http://blog.csdn.net/x_liushi_game/article/details/7538632

 

        题目大意:从A到B有若干个车站,编号从0到m,列车的最大载客量是n。每次列车开车之前,会从各个车站收集订票信息。一共有t条订票信息,一条订票信息包括:起点站,终点站,人数。票价在数值上等于起点站与终点之间的车站数(包括终点站,不包括起点站)。由于列车的最大载客量是一定的,所以不一定能接受所有的订票。对于一条订票order,只能全部接受,或者是全部拒绝。现在选择接受订票使之利润最大,输出这个最大利润。

注:题目出自http://poj.org/problem?id=1040

            分析:这是一个DFS问题,但如果不剪枝的话就很容易TLE。用数组stop[i]表示列车在车站i时车上的总人数。按照订票order的顺序DFS:分别以第1个到第t个order开始,每接受一个order,就把沿线的stop全部加上这个order中的人数,如果发现哪个stop的人数超过了最大载客量n,就必须停下来,这个order是不能接受的,就把stop中加上的人数全部减下来。如果所有沿线的stop都没有超过n,就可以加上该order的利润,开始测试下一个order,测试完成之后取消沿线stop加的人数回溯。每当开始测试order时,就把已经能获得的利润用参数的形式传递过去,保存这个最大值maxe。最后把所有的情况都遍历之后,输出maxe即得结果。

 

 

代码:

#include <iostream> 
#include <cstdlib> 
#include <algorithm> 
using namespace std; 
#define maxm 23 //订单最大23
#define maxn 8 //最大的站为8
int cap, n, m; //cap表示容量,n表示站数,m表示订单数
int ans; //最后结果
int ocount; 
int down[maxn]; //表示在i站下车的人数
struct Order{     
    int s, e, p; 
}order[maxm]; 
  
int max(int a,int b){ 
    return a>b?a:b; 
} 
bool operator < (const Order &a, const Order &b){ 
    if (a.s == b.s)         
        return a.e < b.e;     
    return a.s < b.s; 
} 
  
void dfs(int i, int p, int money){    
    if (i == m){       
        ans = max(ans, money);       
        return;     
    }  
    if (i > 0)//前一个订单下车的减掉         
        for (int j = order[i - 1].s + 1; j <= order[i].s; j++)             
            p -= down[j];  
    if (p + order[i].p <= cap) {//如果加上第i个订单后没有超过火车的容量就可以接受i个订单    
        down[order[i].e] += order[i].p; //down[i]表示i号车站有多少人下车 
        dfs(i + 1, p + order[i].p, money + order[i].p * (order[i].e - order[i].s)); 
        down[order[i].e] -= order[i].p; //恢复现场,以便后面的回溯
    }  
    dfs(i + 1, p, money); //没有接受第i个订单
} 
  
int main(){ 
    int i; 
    while(cin>>cap>>n>>m,cap||n||m){ 
        for(i=0;i<m;i++){    
            scanf("%d%d%d", &order[i].s, &order[i].e, &order[i].p);         
        } 
        sort(order,order+m); 
        ans = 0; 
        dfs(0,0,0); 
        printf("%d\n",ans); 
    } 
    return 0; 
}

作者对DFS算法实现深度解析:

          1、 对order数组排序,使得票的始发站升序排练,当始发站相同时,使得终点站升序排列,这样便于统计每张票的始发站处下车的人数。

         2、 DFS递归函数返回有两种情况:其一是i==m时该条路径搜索结束返回结果到原始调用处;其二是执行到DFS末尾返回,也是返回到调用该次DFS函数处。注意区分34行与37行调用DFS函数的不同,开始选择第1、2、3票搜索路径的DFS函数调用情况如下图所示 

         第34行的DFS调用是考虑选择第i张票,继续深度搜索;第37行的DFS调用是考虑不选择第i张票(可能由于选第i张票超过了火车容量限制或者是前面的搜索选择完毕回溯)调用的地方不同,自然dfs函数返回的地方也不同。

通过本题的分析,总结经验如下:

         算法学习最忌讳粗枝大叶,很多看似思想简单的算法实现成代码运行就有很多新的难题,比如深度搜索DFS的实现,有以下关键点:

         1、由于搜索过程高度重复化,一般写成递归函数,DFS函数里面写明返回条件,不同情况下不同的递归调用条件。

         2、 DFS函数的构造一般有一个变量记录当前搜索的位置(如本题中i记录了当前搜索到了第几张票的选择),另外若干变量保存需要一直维护状态变量(如本题中的p记录了当前火车上的人数,money记录了当前选择方案下的总钱数)

         3、 DFS发生回溯的时候,要注意恢复原来状态变量的值(又叫“恢复现场”),比如第35行      down[order[i].e] -=order[i].p; 这里是由于前面考虑选择第i张票的搜索路径已经搜索完毕,那么下面考虑不选择第i张票,恢复下车人数数组down的状态到不选第i张票的情况,在第37行直接去考察第i+1张票的选择情况。

         最后,对于调用及执行情况比较复杂的代码,用好VC等IDE单步调试,观察callback、watch等窗口中函数调用、关键变量值的变化是很重要的研究途径,调试工具要充分利用。有必要学习一下重要的调试技巧,对于加深对程序的理解有好处。

 

另外关于CSDN上同样的思想,附上代码:

#include <stdio.h>   
#include <string.h>   
int orders[27][4];  
int maxPeoples, endStation, numOrders;;  
int max;  //保存最终结果
int leane[8];  //车上的人
void train(int n, int sum)  
{  
     int i;   
     if (sum > max)  
        max = sum;  
     if(n >= numOrders)  
          return;  
     for (i = orders[n][0]; i < orders[n][1]; i++)  
         if (leane[i]+orders[n][2] > maxPeoples)  //依次扫描第i个订单所经过的站+加上i个订单要上的人
            break;  //如果超过车的容量则退出
     if (i >= orders[n][1]) {  //加上第i个订单的人,并没有超过车的容量,则接受第i个订单
        for (i = orders[n][0]; i < orders[n][1]; i++)  
            leane[i] += orders[n][2];  //将第i个订单上的人更新到leane[],以便继续DFS
        train(n+1, sum+orders[n][3]);  //继续DFS
        for (i = orders[n][0]; i < orders[n][1]; i++)  
            leane[i] -= orders[n][2];  //恢复到没有接收第i个订单的现成,以便回溯
     }  
     train(n+1, sum);  //没有接受第i个订单
}  
int main()  
{  
    int i;  
    int a, b, p;   
    while (scanf("%d%d%d", &maxPeoples, &endStation, &numOrders), maxPeoples||endStation||numOrders) {  
          memset(leane, 0, sizeof(leane));  
          for (i = 0; i < numOrders; i++) {  
              scanf("%d%d%d", &a, &b, &p);  
              orders[i][0] = a;  
              orders[i][1] = b;  
              orders[i][2] = p;  
              orders[i][3] = (b-a)*p;  
          }  
          max = 0;  
          train(0, 0);  
          printf("%d\n", max);  
    }  
    return 0;  
}  

后记:继续加油吧,骚年!
 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值