POJ3666 Making the Grade


题意简述:

已知一个序列$A$ 找到一个单调不增或者单调不减的序列$B$ 使得$\sum_{i=1}^{n}|A_i-B_i|$最小

题解:

这题首先有一个引理

每一个$B_i$都在$A$中出现过

考虑一下简单的证明 设$A’$为$A$排序过后的序列 假如有一段$i~j$对应的$B$不是$A$的值而是夹在$A_i’$和$A_j’$中间的话 那么假设这一段中值比$A_i’$小的有$x$个 值比$A_j’$大的有$y$个 假如$y>x$ 那么我们平移这一段使得最大的到$A_j’$这条线上 那么和中位数问题一样 这样值会变得更小 所以$B$的数值肯定是在$A$中出现过的 那么我们可以考虑一个动态规划

设$f[i][j]$为考虑到第$i$个数对应的数值为$B_j$($B$是排序过后的$A$序列 这里由于可能单调不减或者单调不增所以要dp两次其实数据太水所以一遍就够了)

状态转移方程即为:$f[i][j]=min(f[i-1][k])+abs(a[i]-b[j])$ $(1\leq k\leq j)$

由于在$j$层循环$i$是不变的 所以我们可以考虑一个和最长公共上升序列一样的优化方法其实状态转移方程也很像

综上 时间复杂度为$O(n^2)$

AC代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define ll long long 
using namespace std;
const int maxn=2050;
const ll inf=1e10;
ll n,f[maxn][maxn],a[maxn],b[maxn];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],b[i]=a[i];
    sort(b+1,b+n+1);
    for(int i=1;i<=n;i++)
    {
        ll temp=inf;
        for(int j=1;j<=n;j++)
        {
            temp=min(temp,f[i-1][j]);
            f[i][j]=temp+abs(a[i]-b[j]);
        }
    }
    ll ans=inf;
    for(int i=1;i<=n;i++) ans=min(ans,f[n][i]);

    reverse(b+1,b+n+1);
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
    {
        ll temp=inf;
        for(int j=1;j<=n;j++)
        {
            temp=min(temp,f[i-1][j]);
            f[i][j]=temp+abs(a[i]-b[j]);
        }
    }
    for(int i=1;i<=n;i++) ans=min(ans,f[n][i]);

    cout<<ans<<endl;
    return 0;
} 

文章作者: songhn
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 songhn !
评论
 上一篇
P1156 垃圾陷阱 P1156 垃圾陷阱
1.题解首先该题是一个线性dp问题 由于贪心的考虑我们每次到当前时间有可以取的垃圾都会直接捡起而不会再过一会儿 所以我们可以把每一个出现时间作为策略点 这样从头开始dp 所以我们首先需要对a数组进行排序 排序以后就可以从第一个垃圾到最后一
2019-10-18
下一篇 
CF1228C Primes and Multiplication CF1228C Primes and Multiplication
这场CF我网炸了 然后赛后补题 这道题应该算是魔改一下阶乘质因数分解 题意翻译直接搬洛谷的了懒得打 1.题意简述: 2.题解:首先我们可以把$x$质因数分解 然后考虑$x$的因子$p$ 对于每一个$p$ 我们都要求出$g(1,p)\cd
2019-10-01
  目录