【NKOJ 3699】送披萨

问题描述

何老板开了一家披萨店,有一天突然收到了n个客户的订单。
何老板所在的城市只有一条笔直的大街,我们可以将它想象成数轴,其中位置0是何老板的披萨店,第i个客户所在的位置为Pi,每个客户的位置都不同。如果何老板给第i个客户送披萨,客户会支付Ei-Ti块钱,其中Ti是何老板到达他家的时刻。当然,如果到得太晚,会使得Ei-Ti<0,这时,何老板可以选择不给他送餐,免得他反过来找何老板要钱。
何老板店里面只有一个送餐车(单位时间行驶单位长度的距离),因此只能往返送餐,如下图所示就是一条线路,图中第一行的数字是位置Pi,第二行是Ei。
你的任务是帮助何老板计算出最大的收益。

输入格式

第一行,一个整数n
第二行,n个空格间隔的整数,从左往右给出了每个客户的位置Pi,即P1,P2,……,Pn
第三行,n个空格间隔的整数,从左往右给出了每个客户对应的Ei,即E1,E2,……,En

输出格式

一行,一个整数,表示所求的最佳收益

样例输入 1

5
-6 -3 -1 2 5
27 10 2 5 20

样例输出 1

32

样例输入 2

6
1 2 4 7 11 14
3 6 2 5 18 10

样例输出 2

13

样例输入 3

11
-14 -13 -12 -11 -10 1 2 3 4 5 100
200 200 200 200 200 200 200 200 200 200 200

样例输出 3

1937

提示

1 ≤ n ≤ 100
-100,000 ≤ Pi ≤ 100,000 且Pi!=0
0< Ei ≤ 100,000


题解

这本来是老板的例题,但看不懂老板的代码,就自己写的。。。
除了可以不送披萨,其余的部分都和【Sue的小球 】非常相似。
但可以不送。。。这让费用提前计算变得非常麻烦。
这时,老板冒了出来,吼出记忆化搜索的口头禅:“状态信息量不足,加一维状态!”
所以——f[x][y][cnt][0/1]表示将第x至第y个人处理完(送完了,或者放弃这个披萨)剩下还送cnt个披萨的最大收益。
在讨论时只讨论最左边(最右边)的人送还是不送,这样费用提前计算就简单了(因为知道了会影响之后多少个披萨)。
最后输出max(f[1][n][0][0],f[1][n][0][1])即可。
显然有很多废状态,可用记忆化搜索。
具体见代码。


代码

#include <cstdio>  
#include <iostream>  
#include <algorithm>  
using namespace std;  
const int Q=105;  
struct dt{  
    int p,v;  
}a[Q];  
bool cmp(dt a,dt b)  
{return a.p<b.p;}  
int f[Q][Q][Q][2],st;  
int g(int l,int r,int cnt,int yox)  
{ 
    if(l>st||r<st)return -999999999;  
    if(l==st&&r==st)return 0;  
    if(f[l][r][cnt][yox])return f[l][r][cnt][yox];  
    if(yox==0){  
        int o1=g(l+1,r,cnt,0)-cnt*(a[l+1].p-a[l].p); 
        int o2=g(l+1,r,cnt+1,0)-(cnt+1)*(a[l+1].p-a[l].p)+a[l].v; 
        int o3=g(l+1,r,cnt,1)-cnt*(a[r].p-a[l].p); 
        int o4=g(l+1,r,cnt+1,1)-(cnt+1)*(a[r].p-a[l].p)+a[l].v; 
        f[l][r][cnt][yox]=max(max(o1,o2),max(o3,o4)); 
    }  
    else{ 
        int o1=g(l,r-1,cnt,1)-cnt*(a[r].p-a[r-1].p); 
        int o2=g(l,r-1,cnt+1,1)-(cnt+1)*(a[r].p-a[r-1].p)+a[r].v; 
        int o3=g(l,r-1,cnt,0)-cnt*(a[r].p-a[l].p); 
        int o4=g(l,r-1,cnt+1,0)-(cnt+1)*(a[r].p-a[l].p)+a[r].v; 
        f[l][r][cnt][yox]=max(max(o1,o2),max(o3,o4)); 
    }  
    return f[l][r][cnt][yox];  
}  
int main()  
{  
    int i,n;  
    scanf("%d",&n);  
    ++n;  
    for(i=1;i<n;i++)scanf("%d",&a[i].p);  
    for(i=1;i<n;i++)scanf("%d",&a[i].v);  
    a[n].p=0;  
    sort(a+1,a+n+1,cmp);  
    for(i=1;i<=n;i++)if(a[i].p==0){  
        st=i;break;  
    }  
    printf("%d",max(g(1,n,0,0),g(1,n,0,1))) ; 
    return 0;
}
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值