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:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }