动态规划两题浅析
Q1 [AtCoder-4522]
有一蛙,立于1号岩石上,目标是要跳到N号石头上,每次它可以跳到前边一块或隔一块。两石头的高度差的绝对值是青蛙每次跳跃的代价,试问青蛙到终点时可以消耗的最小代价值为多少
解析
动规一般从小规模开始算,假设蛙在第N-1的石头上,只有1种跳法,即代价为cost[N-1]=abs(h[N-1]-h[N]),而当他在第N-2块石头上,它就可以选择直接跳,或跳到N-1上再花费cost[N-1],所以cost[N-2]等于abs(h[N-2]-h[N])与 abs(h[N-2]-h[N-1])+cost[N-1]的较小值,归纳得到当它在第i块岩石上的时候,它的跳到终点的代价为min( abs(h[i]-h[i+2])+cost[i+2],
abs(h[i]-h[i+1]) +cost[i+1]) ,使i从N-1遍历到0,输出cost[1]即为青蛙从第1号岩石到第N号岩石的最小代价
#include <cstdio>
#include <algorithm>
using std::min;
int m_abs(int z)
{
if (z<0)
return -z;
else
return z;
}
int main(void)
{
int *a,*dp,N,i,j;
scanf("%d",&N);
a=new int [N];
dp=new int [N];
for (i=0;i<N;i++)
{
scanf("%d",a+i);
}
dp[N-1]=0;
dp[N-2]=abs(a[N-2]-a[N-1]);
for(i=N-3;i>=0;i--)
{
dp[i]=min(dp[i+1]+abs(a[i]-a[i+1]),dp[i+2]+abs(a[i]-a[i+2]));
}
printf("%d\n",dp[0]);
delete [] a;
}
Q2 [AtCoder-4523]
有一只更强的蛙,立于1号岩石之上,它的目标是跳到N号岩石上,假定当前脚下的编号为i,它可以跳到编号为i+1,i+2,…,i+K的岩石上(Q1即是此处K=2的情形),跳跃代价为两岩石的高度之差,计算青蛙到终点的最小代价
分析
与Q1类似,仍应从N-1,N-2,…N-k开始计算,N-1只有1种跳法,N-2有两种跳法,在N-1确定的情况下N-2也可确定,以此推算出N-k的代价。再往前若青蛙站在了一次跳不到终点的地方,就去看它接下来可跳的K个岩石的代价与从这个岩石跳到那边的代价之和的最小值,算到cost[1]即它从第1号岩石上所使用的最小代价
#include <cstdio>
#include <algorithm>
using std::min;
int m_abs(int z)
{
if (z<0)
return -z;
else
return z;
}
int main(void)
{
int *a,*dp,N,i,j,K;
scanf("%d%d",&N,&K);
a=new int [N];
dp=new int [N];
for (i=0;i<N;i++)
{
scanf("%d",a+i);
}
dp[N-1]=0;
if(K>N)
K=N;
for(i=0;i<K;i++)
dp[N-1-i]=abs(a[N-1]-a[N-1-i]);
for(i=N-1-K;i>=0;i--)
{
dp[i]=dp[i+1]+abs(a[i]-a[i+1]);
for(j=2;j<=K;j++)
{
dp[i]=min(dp[i+j]+abs(a[i]-a[i+j]),dp[i]);
}
//printf("dp[%d]= %d\n",i,dp[i]);
}
printf("%d\n",dp[0]);
delete [] dp;
delete [] a;
return 0;
}