POJ_3666_Making_the_Grade_(动态规划)

时间:2023-03-08 15:23:15
POJ_3666_Making_the_Grade_(动态规划)

描述


http://poj.org/problem?id=3666

给一串坡的高度,现在要调整某些点,使整个坡单调不降或单调不升.调整的花费为原高度与先高度的差的绝对值,问最小花费(可单增可单降).

Making the Grade
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 5802   Accepted: 2717

Description

A straight dirt road connects two fields on FJ's farm, but it changes elevation more than FJ would like. His cows do not mind climbing up or down a single slope, but they are not fond of an alternating succession of hills and valleys. FJ would like to add and remove dirt from the road so that it becomes one monotonic slope (either sloping up or down).

You are given N integers A1, ... , AN (1 ≤ N ≤ 2,000) describing the elevation (0 ≤ Ai ≤ 1,000,000,000) at each of N equally-spaced positions along the road, starting at the first field and ending at the other. FJ would like to adjust these elevations to a new sequence B1, . ... , BN that is either nonincreasing or nondecreasing. Since it costs the same amount of money to add or remove dirt at any position along the road, the total cost of modifying the road is

|A1 - B1| + |A2 - B2| + ... + |AN - BN |

Please compute the minimum cost of grading his road so it becomes a continuous slope. FJ happily informs you that signed 32-bit integers can certainly be used to compute the answer.

Input

* Line 1: A single integer: N
* Lines 2..N+1: Line i+1 contains a single integer elevation: Ai

Output

*
Line 1: A single integer that is the minimum cost for FJ to grade his
dirt road so it becomes nonincreasing or nondecreasing in elevation.

Sample Input

7
1
3
2
4
5
3
9

Sample Output

3

Source

分析


分单调不增和单调不降两种情况,是一样的,我们分析单调不降的情况.

用dp[i][j]表示前i个点有序且以j结尾的最小花费.则有转移方程:

dp[i][j]=min(dp[i-1][k])+abs(a[i]-j) (0<=k<=j).

但是看数据范围发现高度可以取到10^9,而且分析可知,一个点如果需要调整,为了花费最小,只需要和左边一样就好,所以调整之后的取值一定在a数组中,显然要离散一下.

用dp[i][j]表示前i个点有序且以b[i]结尾的最小花费.则有转移方程:

dp[i][j]=min(dp[i-1][k])+abs(a[i]-a[j])(a[k]<=a[j]).

这样的话就需要三层i,j,k的循环,会超时,考虑把a数组copy一份到b,然后把b升序排列一下,这样在第二层循环里统计k<=j即b[k]<=b[j]的最小的dp[i-1][k].另外,由于只用到了i和i-1,所以可以考虑使用滚动数组.

ps.

1.POJ上数据有问题,单调不降一遍就能过,实际上应该dp两遍.

2.感觉自己好弱啊,动规基本都是看题解才做出来的= =,我这样强行作死真的大丈夫?

 #include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define for1(i,a,n) for(int i=(a);i<=(n);i++)
#define read(a) a=getnum()
#define CC(i,a) memset(i,a,sizeof(i))
using namespace std; const int maxn=+,INF=0x7fffffff;
int n;
int a[maxn],b[maxn];
int dp[][maxn]; inline int getnum()
{
int r=,k=; char c;
for(c=getchar();c<''||c>'';c=getchar()) if(c=='-') k=-;
for(;c>=''&&c<='';c=getchar()) r=r*+c-'';
return r*k;
} bool comp(int a,int b) { return a>b; } void solve()
{
sort(b+,b+n+);
for1(i,,n) dp[][i]=abs(a[]-b[i]);
for1(i,,n)
{
int min_c=dp[(i-)&][];
for1(j,,n)
{
min_c=min(min_c,dp[(i-)&][j]);
dp[i&][j]=min_c+abs(a[i]-b[j]);
}
}
int ans=INF;
for1(i,,n) ans=min(ans,dp[n&][i]);
sort(b+,b+n+,comp);
for1(i,,n) dp[][i]=abs(a[]-b[i]);
for1(i,,n)
{
int min_c=dp[(i-)&][];
for1(j,,n)
{
min_c=min(min_c,dp[(i-)&][j]);
dp[i&][j]=min_c+abs(a[i]-b[j]);
}
}
for1(i,,n) ans=min(ans,dp[n&][i]);
printf("%d\n",ans);
} void init()
{
read(n);
for1(i,,n)
{
read(a[i]);
b[i]=a[i];
}
} int main()
{
#ifndef ONLINE_JUDGE
freopen("making.in","r",stdin);
freopen("making.out","w",stdout);
#endif
init();
solve();
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
system("making.out");
#endif
return ;
}