Making the Grade [POJ3666] [DP]

题意:

给定一个序列,以最小代价将其变成单调不增或单调不减序列,代价为Σabs(i变化后-i变化前),序列长度<=2000,单个数字<=1e9

输入:(第一行表示序列长度,之后一行一个表示序列第i的大小)

7
1
3
2
4
5
3
9
输出:(代价)
  3
分析:
这道题有bug,只要求单调不减序列
首先,对于这种问题,我们容易想到DP
记dp[i][j]为处理到i个,最高的为j
那我们的dp[i][j]=min(dp[i-1][k])+abs(j-h[i]) (k<=j)
但是显然数字太大了,我们需要离散化
先把序列从小到大排序,存在另一个数组b[]里
把j记成第j大的,那么状态转移方程为dp[i][j]=min(dp[i-1][k])+abs(b[j]-h[i]) (k<=j)
这样空间复杂度就够了
但时间复杂度还不够
我们仔细观察可以发现,dp[i-1][k]的每次从1开始循环找最小值是浪费的
我们在j从小到大循环上来的时候,就记录下最小值mn,那么转移方程就优化成dp[i][j]=mn+abs(b[j]-h[i])
至此,本题解决。
(提示:开long long,inf要开大!)
Code:
 1 #include<set>
 2 #include<map>
 3 #include<queue>
 4 #include<stack>
 5 #include<cmath>
 6 #include<cstdio>
 7 #include<cstring>
 8 #include<iostream>
 9 #include<algorithm>
10 #define RG register ll
11 #define rep(i,a,b)    for(RG i=a;i<=b;++i)
12 #define per(i,a,b)    for(RG i=a;i>=b;--i)
13 #define ll long long
14 #define inf (1<<30)
15 #define maxn 2005
16 using namespace std;
17 ll n;
18 ll a[maxn],b[maxn],dp[maxn][maxn];
19 inline ll read()
20 {
21     ll x=0,f=1;char c=getchar();
22     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
23     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
24     return x*f;
25 }
26 
27 void work()
28 {
29     rep(i,1,n)
30     {
31         ll mn=inf;
32         rep(j,1,n)
33         {
34             mn=min(mn,dp[i-1][j]);
35             dp[i][j]=(a[i]-b[j]>=0?a[i]-b[j]:b[j]-a[i])+mn;
36         }
37     }
38     ll ans=inf;
39     rep(i,1,n) ans=min(ans,dp[n][i]);
40     cout<<ans;
41 }
42 
43 int main()
44 {
45     n=read();
46     rep(i,1,n) a[i]=b[i]=read();
47     sort(b+1,b+1+n);
48     work();
49     return 0;
50 }
View Code

猜你喜欢

转载自www.cnblogs.com/ibilllee/p/9220613.html