uva--301+dfs回溯穷举

题意:

    一列火车有n个座位,它的行驶路线上有m个售票站,对于每个售票站售出的火车票,可以选择全部接受或者全部不接受;每张火车票的价格

为起点与终点站之间的站数差;问这样选择售票站才能使收益最大?

思路:

    显然对于每个售票站,都可以选择接受其所有票或者不接受;m<=22 所以可以用dfs穷举所有的方案。具体是:先按起点站对所有售票站按升序排序,用cnt记录当前火车上的人数,sum记录当前的收入,cur记录当前要判断的售票站的编号,vis[]记录每一站应该下车的人数;对于每一个售票站如果满足要求,就可以选择,当然也可以不选,对于其它情况都是不用选择,如此进行dfs。当然这里的重点在于判断当前的售票站是否满足要求,一种方法就是不在每一站对下车的情况进行处理而是通过用cnt-该站之前应该下车的人数是否大于n进行判断;另一种方法是,每一次不管选不选择售票站都对下车的情况进行处理,每次只关注此次的起点站和上次的起点站,这样可以避免重复处理。

反思:

   在这种dfs穷举的题目中,进行判断时判断的方式一定也要是可以递归回溯的,否则判断就会出错,比如下面的第三份代码,判断完后直接赋0,就会导致回溯的时候出错。


代码如下:


#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

int ans,vis[10],m,total,n;
typedef struct
{
    int x,y;
    int cnt;
}P;
P p[30];

int cmp(P p1,P p2)
{
    if(p1.x==p2.x)
        return p1.y<p2.y;
    return p1.x<p2.x;
}

int check(int cur,int cnt)
{
    int sum=0,i;
    for(i=0;i<=p[cur].x;i++)
        sum+=vis[i];
    if(cnt-sum+p[cur].cnt<=total)
        return 1;
    return 0;
}

void dfs(int cur,int sum,int cnt)
{
    int j;
    if(cur==m)
    {
        if(ans<sum)
            ans=sum;
        return ;
    }
    if((check(cur,cnt)))
    {
        vis[p[cur].y]+=p[cur].cnt;
        dfs(cur+1,sum+p[cur].cnt*(p[cur].y-p[cur].x),cnt+p[cur].cnt);
        vis[p[cur].y]-=p[cur].cnt;
    }
    dfs(cur+1,sum,cnt);
}

int main()
{
    int i,j;
    while(scanf("%d%d%d",&total,&n,&m))
    {
        if(total==0&&n==0&&m==0)
            break;
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].cnt);
        }
        sort(p,p+m,cmp);
        ans=0;
        memset(vis,0,sizeof(vis));
        dfs(0,0,0);
        printf("%d\n",ans);
    }
  return 0;
}

代码2:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

int ans,vis[10],m,total,n;
typedef struct
{
    int x,y;
    int cnt;
}P;
P p[30],p1;

int cmp(P p1,P p2)
{
    if(p1.x==p2.x)
        return p1.y<p2.y;
    return p1.x<p2.x;
}

void dfs(int cur,int sum,int cnt,int pre)
{
    int j;
    if(cur==m)
    {
        if(ans<sum)
            ans=sum;
        return ;
    }
    //无论这个车站上不上人,下车都是必须的
    for(j=pre+1;j<=p[cur].x;j++)
       cnt-=vis[j];
    if(cnt+p[cur].cnt<=total)
    {
        vis[p[cur].y]+=p[cur].cnt;
        dfs(cur+1,sum+p[cur].cnt*(p[cur].y-p[cur].x),cnt+p[cur].cnt,p[cur].x);
        vis[p[cur].y]-=p[cur].cnt;
    }
    dfs(cur+1,sum,cnt,p[cur].x);
}

int main()
{
    int i,j;
    while(scanf("%d%d%d",&total,&n,&m))
    {
        if(total==0&&n==0&&m==0)
            break;
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].cnt);
        }
        sort(p,p+m,cmp);
        ans=0;
        memset(vis,0,sizeof(vis));
        dfs(0,0,0,0);
        printf("%d\n",ans);
    }
  return 0;
}


错误代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

int ans,vis[10],m,total,n;
typedef struct
{
    int x,y;
    int cnt;
}P;
P p[30],p1;

int cmp(P p1,P p2)
{
    if(p1.x==p2.x)
        return p1.y<p2.y;
    return p1.x<p2.x;
}

void dfs(int cur,int sum,int cnt)
{
    int j;
    if(cur==m)
    {
        if(ans<sum)
            ans=sum;
        return ;
    }
    //无论这个车站上不上人,下车都是必须的
    //下车之后记得将相应车站人数清0
    for(j=0;j<=p[cur].x;j++)
    {
        cnt-=vis[j];
        vis[j]=0;//赋0操作导致回溯出错
    }
    if(cnt+p[cur].cnt<=total)
    {
        vis[p[cur].y]+=p[cur].cnt;
        dfs(cur+1,sum+p[cur].cnt*(p[cur].y-p[cur].x),cnt+p[cur].cnt);
        vis[p[cur].y]-=p[cur].cnt;
    }
    dfs(cur+1,sum,cnt);
}

int main()
{
    int i,j;
    while(scanf("%d%d%d",&total,&n,&m))
    {
        if(total==0&&n==0&&m==0)
            break;
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].cnt);
        }
        sort(p,p+m,cmp);
        ans=0;
        memset(vis,0,sizeof(vis));
        dfs(0,0,0);
        printf("%d\n",ans);
    }
  return 0;
}


  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值