题目链接:
https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1270
一、题目要求解读
这道题的意思是,我现在给定一个数组B,这个数组里面有N个元素。现在定义数组A,数组A同样要求有N项,要求这N项的每一项都必须大于等于1,且小于等于那一项相应B数组里的值,即1<=Ai<=Bi。当然,一般情况下,这样的数组A肯定不止有一个,所以代价S也就会有多个。那么肯定有一个最大的S,现在就让你求这个最大的S,但是不需要你给出最大的S对应的数组A。
二、思路与算法
最简单的思路就是穷举法,按照定义求出所有的数组A,然后分别计算代价S,在找最大。当然,这肯定是会超时的啦~~~
作为一枚算法渣渣,我其实一开始并没有想到用动态规划,是看到这个题的标签里有动态规划才向这个方向想的。
既然要用动态规划,那么肯定要想状态怎么搞。
我的第一版思路是:dp[i][j]::=前i项构成的子问题中,当最后一项Ai=j时的最大代价
那么就会有递推关系:
dp[1][1] = 0 dp[1][2] = 0 dp[1][3] = 0 .........
dp[i][j] = max(dp[i-1][1] + abs(j-1), dp[i-1][2] + abs(j-2), ...,dp[i-1][B(i-1)] + abs(j-B(i-1)))
项数的规模是50000,每一项的规模是10000,那么这个问题的规模大约是50000*10000,这样考虑状态肯定是要超时的,所以就不要想怎么写代码了。
我的下一个思路的可行性就高多了。
dp[i][0]::=前i项构成的子问题中,当Ai=1时的最大代价
dp[i][1]::=前i项构成的子问题中,当Ai=Bi时的最大代价
我们可以想象,能够得到最大代价的序列肯定是需要每一步都取极端值的,这样才能尽可能让波动最大化。
递推关系:
dp[1][0] = dp[1][1] = 0
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + abs(1-B(i-1)))
dp[i][1] = max(dp[i-1][1] + abs(Bi-B(i-1)), dp[i-1][0] + abs(Bi-1))
因为递推关系只存在于相邻的两项之间,所以dp数组的规模可以限定在2*2之内。
算法的时间复杂度就是O(n)。可见,这个算法是一个时间和空间上都很优秀的算法。动态规划赛高!
三、C语言源代码
#include <stdio.h>
#include <stdlib.h>
#define MAX(x, y) (x)>(y) ? (x) : (y)
int main()
{
int i, n, b[50000], dp[2][2];
scanf("%d", &n);
for(i=0;i<n;i++)
scanf("%d", b+i);
dp[0][0] = 0;
dp[0][1] = 0;
for(i=1;i<n;i++)
{
dp[i%2][0] = MAX(dp[(i-1)%2][0], dp[(i-1)%2][1] + abs(1-b[i-1]));
dp[i%2][1] = MAX(dp[(i-1)%2][1] + abs(b[i]-b[i-1]), dp[(i-1)%2][0] + abs(1-b[i]));
}
printf("%d", MAX(dp[(n-1)%2][0], dp[(n-1)%2][1]));
return 0;
}