OJ1952《[usaco2008feb_gold]路面修整》题解



因为有幸做了这道题的沙发,只好写个题解,把题目讲讲。

分析:
因为题目要求改成不下降或不上升序列,所以需要做两遍DP,我们这里以不下降序列为例。
首先需要明确,修正后的路面一定是原路面的某个值。所以就可以再开一个b数组,是原数组的上升排序版。
dp是一维的显然不行,dp[i][j]表示将前i段路全部改变成满足题意的情况(不下降序列),并且使第i段路在新序列中高度从小到大排第j名,也就是说改变之后i是b数组中的第j个数。(i,j<=N),这样得出了初始的状态转移方程: dp[i][j]=min(dp[i-1][k]+abs(a[i]-b[i]));  (k<=j)
上面的算法看似很完美,但是时间是O(N*N*N)的,N<=2000,显然会超时,下面我们考虑一下优化。
我们把dp的定义做一个改变,dp[i][j]表示的是前i段路满足题意的情况下,使第i段路的高度小于或等于j,
这样状态转移方程就变为了: dp[i][j]=min(dp[i][j-1],dp[i-1][j]+abs(b[j]-a[i]));
这样O(N*N)就可以解决掉了,ans=dp[N][N]。
再做一遍不上升的就行,状态转移方程不变,只用把b数组重新排序即可!!!
注意:dp数组的初值,f[i][0]=0x7fffffff!

代码如下:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define INF 0x7fffffff
using namespace std;
int N,a[2005],b[005],ans=INF,c[2005][2005],f[2005][2005];
inline bool cmp(int a,int b){return a>b;}
void init()
{
	scanf("%d",&N);
	for(int i=1;i<=N;i++)
	{
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
    sort(b+1,b+N+1);
    for(int i=1;i<=N;i++) f[i][0]=INF;
}
void DP()
{
	for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++)
        {
            c[i][j]=f[i-1][j]+abs(b[j]-a[i]);
            f[i][j]=min(f[i][j-1],c[i][j]);
        }
    ans=f[N][N];
    sort(b+1,b+N+1,cmp);
    for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++)
        {
            c[i][j]=f[i-1][j]+abs(b[j]-a[i]);
            f[i][j]=min(f[i][j-1],c[i][j]);
        }
    ans=min(ans,f[N][N]);
    printf("%d\n",ans);
}
int main()
{
	init();
	DP();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Dante__Alighieri/article/details/44157529
今日推荐