100道动态规划——32 UVA 12170 Easy Climb 通过分析减少状态数 单调队列

        好吧,不会做

        一开始想到的状态是定义dp[i][j]表示第i个数修改为j时的最小值,然后因为d范围过大就把这个想法否定掉了,然后就不会做了

        否定的理由是①:数组开不下②:对于一个j要考虑[j-d,j+d]之间的数,状态太多了,状态总数显然是Ω(n*d)的

        确实没有想到通过分析来减少状态的数量,而且是大大减少

        首先来确定impossible的情况,无妨设h1<hn,我们从左向右考虑,考虑impossible时hn的最小值

        显然h2的最大值只能是h1+d,h3的最大值只能是h2+d即h1+2d........h(n-1)的最大值是h1+(n-2)d,hn的最大值是h1+(n-1)d

        我们在上述增加的过程中,每次都增加了最大的增量,因此hn最大不能超过h1+(n-1)d,假若超过了这个值,便无论如何也加不到了,便是impossible

        接下来考虑减少状态,即对于中间的hj,我们考虑能把它改到哪些位置

        突然发现数学归纳法真是一个比较好的办法——仅仅在证明的意义下

        当仅仅有h1,h2,h3时,中间的h2只有3个状态,即可以写成hp+k*d的形式,其中p∈[1,n],k∈(-n,n)

        归纳猜想是有k个数时,里面每一个都能写成hp+k*d的形式

        当向里面增加一个数时,无妨设加到i位置上,因此hi可以变成hi,max(h(i-1),h(i+1))+d,min(h(i-1),h(i+1))-d,也就是说,hi同样的也可以写成hp+k*d的形式

        我觉得这个证明是没错的。。。

        至于问为什么想到的。。。我也不能回答你

        

        第二件事:状态转移方程

        显然dp[i][j]=|hi-j|+min(dp[i-1][k])其中j-d<=k<=j+d

        单调队列嘛,看着代码很好理解的,我把我想不到的重点说完了,这里就自己看代码好了,不过老实说我就算想到了减状态也不会想到单调队列。

        单调队列优化挺大的

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;
using ll=long long;
const int maxm=105;
const ll INF=(1LL<<60);


ll times,n,d,h[maxm],nx,t,dp[2][maxm*maxm*2],x[maxm*maxm*2];
void pretreatment();

int main(){
    ios_base::sync_with_stdio(false);
    cin>>times;
    while(times--){
        cin>>n>>d;
        for(int i=0;i<n;++i)
            cin>>h[i];

        if(labs(h[0]-h[n-1])>(n-1)*d){
        	cout<<"impossible\n";
        	continue;
        }
        pretreatment();

        for(int i=1,k=0;i<n;++i,k=0,t^=1)
        for(int j=0;j<nx;++j){
        	while(k<nx&&x[k]<x[j]-d)++k;
        	while(k+1<nx&&x[k+1]<=x[j]+d&&dp[t][k+1]<=dp[t][k])++k;
        	dp[t^1][j]=(dp[t][k]==INF)?INF:labs(h[i]-x[j])+dp[t][k];
        }

        for(int i=0;i<nx;++i)
        if(x[i]==h[n-1])
        	cout<<dp[t][i]<<endl,i=nx;
    }
    return 0;
}

void pretreatment(){
	nx=0;
	for(int i=0;i<n;++i)
	for(int j=-n+1;j<=n-1;++j)
		x[nx++]=h[i]+j*d;
	sort(x,x+nx);
	nx=unique(x,x+nx)-x;

	for(int i=0;i<nx;++i){
		dp[t][i]=INF;
		if(h[0]==x[i])
			dp[t][i]=0;
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值