[noip模拟题]仔排

Description
wpf深知买仔排的不易(食堂好黑的!),于是经过深思熟虑,wpf决定自己卖仔排!想我们wpf可是从事经济研究的,做买卖可难不倒wpf!经过几个日夜的钻研,wpf得出一个很重要的结论:低价买,高价卖。一块仔排进价只有1元,如果以2元卖出,不就赚钱了嘛!接下来,就要考虑运货问题了。手推车既轻便又环保,乃是居家旅行做生意之必备武器,正巧wpf就有一辆。每次,他都可以去批发商那里运一车仔排到常外去。可惜wpf力气有限,一次只能运最多K块仔排(手推车很轻,重量不计),再多wpf就推不动了。最开始,wpf手中有X元人民币。之后的每天,wpf都有三种选择:买进若干块仔排,卖出若干块仔排,或者自己吃掉一些仔排。三种选择中,他每天只能选一种。仔排的价格是固定的,买进1元,卖出2元。他可以一次买进任意数量的仔排,只要他有足够的钱,并且买下这批仔排后,他所有仔排的数量不能超过K,也不能超过V(wpf是用手推车存仔排的,多了存不下,重了推不动)。如果他当日手中有仔排,他也可以选择卖出任意数量,因为常外的小朋友们是那么热爱椒盐仔排,卖多少都会被抢购一空。他还可以选择吃掉手中部分或全部的仔排,这样一来,wpf就会迅速长壮,他一次就可以运送更多货物。说具体一些,每吃一块仔排,最大运货量K的值就会增加1。当然,手推车有个容量V,即使wpf的力气再大,一次运送仔排的数量也不能超过手推车的容量–也就是说,买进的仔排数量总是不超过V的。钱是好东西呀。不是有句话这么说来着:wpf赚钱,多多益善。那么在N天以内,wpf最多能得到多少钱呢?

Input
输入只有一行,包含了3个整数,依次为NXKV
N表示做生意的天数,X表示初始的资金,而K表示一开始wpf能运的最大仔排数量,V表示手推车的容量。
N100X100K50V100

Output
输出一个整数,表示N天后wpf手中最多的现金数量。

Sample Input
8 5 1 5

Sample Output
10

提示
第一天买进一个仔排,花掉一块钱,手上还有4块钱。
第二天吃掉一个仔排,最大装货值变为2。
第三天买2个仔排,剩下2块钱。
第四天卖2个仔排,手上有6块钱。
第五到八天重复这一过程。
最后是10块钱。
这是最优方案。

HINT
DP

思路
看到数据范围,这道题时间复杂度肯定不小。实际上这道题的时间复杂度是O(nv3)的。令fi,j,k表示第i天结束后,拥有j块仔排,现在的最大容量为k的最多拥有的钱的数量。那么状态转移方程就是:
如果买入仔排:fi,j,k=max(fi,j,k,fi1,L,k(jL))(其中L为变量,0<=L<=j
如果卖出仔排:fi,j,k=max(fi,j,k,fi1,L,k+(Lj)2)(其中L为变量,j<=L<=k
如果吃掉仔排:fi,j,k=max(fi,j,k,fi1,jL,k+L)(其中L为变量,0<=L<=min(vj,k)
这三个转移方程应该很好理解吧,注意一点:如果出现了负数是不行的,必须把这个较小的负数换成一个极大的负数(inf),避免后面的状态由这个负数转移过来。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>

const int maxn=100;
const int inf=2000000000;

int n,x,m,v,ans;
int f[maxn+10][maxn+10][maxn+10];

int main()
{
    scanf("%d%d%d%d",&n,&x,&m,&v);
    memset(f[0],128,sizeof f);
    f[0][0][std::min(v,m)]=x;
    for(int i=1; i<=n; i++)
    {
        for(int j=0; j<=v; j++)
        {
            for(int k=j; k<=v; k++)
            {
                for(int l=0; l<=j; l++)
                {
                    f[i][j][k]=std::max(f[i][j][k],f[i-1][l][k]-(j-l));
                }
                for(int l=j; l<=k; l++)
                {
                    f[i][j][k]=std::max(f[i][j][k],f[i-1][l][k]+(l-j)*2);
                }
                for(int l=0; l<=std::min(v-j,k); l++)
                {
                    f[i][j][k]=std::max(f[i][j][k],f[i-1][j+l][k-l]);
                }
                if(f[i][j][k]<0)
                {
                    f[i][j][k]=-inf;
                }
            }
        }
    }
    for(int i=0; i<=v; i++)
    {
        for(int j=0; j<=v; j++)
        {
            ans=std::max(ans,f[n][i][j]);
        }
    }
    printf("%d\n",ans);
    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值