描述
给一串坡的高度,现在要调整某些点,使整个坡单调不降或单调不升.调整的花费为原高度与先高度的差的绝对值,问最小花费(可单增可单降).
分析
分单调不增和单调不降两种情况,是一样的,我们分析单调不降的情况.
用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=2000+5,INF=0x7fffffff; int n; int a[maxn],b[maxn]; int dp[2][maxn]; inline int getnum() { int r=0,k=1; char c; for(c=getchar();c<‘0‘||c>‘9‘;c=getchar()) if(c==‘-‘) k=-1; for(;c>=‘0‘&&c<=‘9‘;c=getchar()) r=r*10+c-‘0‘; return r*k; } bool comp(int a,int b) { return a>b; } void solve() { sort(b+1,b+n+1); for1(i,1,n) dp[1][i]=abs(a[1]-b[i]); for1(i,2,n) { int min_c=dp[(i-1)&1][1]; for1(j,1,n) { min_c=min(min_c,dp[(i-1)&1][j]); dp[i&1][j]=min_c+abs(a[i]-b[j]); } } int ans=INF; for1(i,1,n) ans=min(ans,dp[n&1][i]); sort(b+1,b+n+1,comp); for1(i,1,n) dp[1][i]=abs(a[1]-b[i]); for1(i,2,n) { int min_c=dp[(i-1)&1][1]; for1(j,1,n) { min_c=min(min_c,dp[(i-1)&1][j]); dp[i&1][j]=min_c+abs(a[i]-b[j]); } } for1(i,1,n) ans=min(ans,dp[n&1][i]); printf("%d\n",ans); } void init() { read(n); for1(i,1,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 0; }
时间: 2024-10-14 20:13:36