HDU - 6652 Getting Your Money Back (dp+单调优化)

题目:click
题意:给定一个[x,y]的区间,让你拿走你的余额如果猜的x小于等于你的余额,则花费a取出来,不然花费b无法取钱。问最坏的情况你花费的最小值。

初看题目有点像二分的题目,但是a,b的值是不相同的,你可以疯狂取a去确定余额当a很小的时候,或者b小疯狂取b去确定,都是不确定的,当a==b的时候是可以直接二分取中点的。不确定取哪个点我就直接枚举取点的情况,像一个区间dp,dp[i][j]表示这个区间的最坏情况最小值,但显然数据范围是不允许的,进一步看取完i后成立则是区间[i+1,y] 否则区间[0,i-1],区间的位置并不影响我的答案,影响状态的是区间的长度。

按照集合分为从0开始的区间,以及不从0开始的区间,进一步用长度区分区间递推,因为0端点可以不用考虑0表示没有余额也不用再取了,dp[i][0]表示区间的跨度为i的(长度貌似不太好做,考虑0端点),左端点从0开始的区间的最坏情况的最小花费。
可以推导出:
dp[i][0]=min( max(dp[i-j][0]+a,dp[j-1][0]+b) )
dp[i][1]=min( max(dp[i-j][0]+a,dp[j-1][1]+b) )
dp[0][0]=0,dp[0][1]=a;
区间长度明显越长花费就越多,max()中前面的显然是递减的,后面的是递增的随j的增大,可以画个图,按道理三分就可以解决,但是RE了,不知道为什么,大佬们如果发现错误麻烦指点我一下。进一步考虑,做完图发现随着i的增大最优j的点是单调不下降的,每次考虑向右边移动,做一个单调的优化。
附上我三分的代码,请各位帮忙看看,指点错误,写的比较丑。。。。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll dp[200100][2];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ll x,y,a,b,i,j,k;
        scanf("%lld %lld %lld %lld",&x,&y,&a,&b);
        for(i=0;i<=y-x;i++)
            dp[i][0]=dp[i][1]=llinf;
                dp[0][0]=0;
                dp[0][1]=a;
        j=1;
        k=1;
        for(i=1;i<=y-x;i++)
        {
            while(j<i&&max(dp[j-1][0]+b,dp[i-j][0]+a)>=max(dp[j][0]+b,dp[i-j-1][0]+a))
            {
                ++j;
            }
            while(k<i&&max(dp[k-1][1]+b,dp[i-k][0]+a)>=max(dp[k][1]+b,dp[i-k-1][0]+a))
            {
                ++k;
            }
            dp[i][0]=max(dp[j-1][0]+b,dp[i-j][0]+a);
            dp[i][1]=max(dp[k-1][1]+b,dp[i-k][0]+a);
        }
        if(x==0)
        printf("%lld\n",dp[y-x][0]);
        else
        printf("%lld\n",dp[y-x][1]);
    }
    return 0;
}

错误三分代码:

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll dp[200100][2];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ll x,y,a,b,i,j;
        scanf("%lld %lld %lld %lld",&x,&y,&a,&b);
        for(i=0;i<=y-x;i++)
            dp[i][0]=dp[i][1]=llinf;
                dp[0][0]=0;
                dp[0][1]=a;
        for(i=1;i<=y-x;i++)
        {
            ll l=1,r=y-x;
            while(l<=r)
            {
                    ll lmid=(2*l+r)/3;
                    ll rmid=(2*r+l)/3;
                    if(max(dp[lmid-1][0]+b,dp[i-lmid][0]+a)<max(dp[rmid-1][0]+b,dp[i-rmid][0]+a))
                    {
                        dp[i][0]=min(dp[i][0],max(dp[lmid-1][0]+b,dp[i-lmid][0]+a));
                        r=rmid-1;
                    }
                    else
                    {
                        dp[i][0]=min(dp[i][0],max(dp[rmid-1][0]+b,dp[i-rmid][0]+a));
                        l=lmid+1;
                    }
            }
             dp[i][0]=min(dp[i][0],max(dp[l-1][0]+b,dp[i-l][0]+a));
            l=1,r=y-x;
            while(l<=r)
            {
                    ll lmid=(2*l+r)/3;
                    ll rmid=(2*r+l)/3;
                    if(max(dp[i-lmid][0]+a,dp[lmid-1][1]+b)<max(dp[i-rmid][0]+a,dp[rmid-1][1]+b))
                    {
                        dp[i][1]=min(dp[i][1],max(dp[i-lmid][0]+a,dp[lmid-1][1]+b));
                        r=rmid-1;
                    }
                    else
                    {
                        dp[i][1]=min(dp[i][1],max(dp[i-rmid][0]+a,dp[rmid-1][1]+b));
                        l=lmid+1;
                    }
            }
            dp[i][1]=min(dp[i][1],max(dp[i-l][0]+a,dp[l-1][1]+b));
        }
        if(x==0)
        printf("%lld\n",dp[y-x][0]);
        else
        printf("%lld\n",dp[y-x][1]);
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值