zoukankan      html  css  js  c++  java
  • 「Algospot」量化QUANTIZE

    一道不难的DP题,主要是为了总结这类最优化题的思路;同时还学到了一个新操作

    传送门:$>here<$

    题意

    给出一个长度为$N$的序列,要求最多使用s个数字进行量化(有损压缩),即代替原数字:使得量化后各个被代替的数与代替的数的差的平方之和最小。

    数据范围:$n leq 100, s leq 10$

    Solution

    动态规划的常规思路

    容易发现肯定选择数值相近的一些数字用一个数字取替代,所以肯定先排序。

    排序完后就是一个简单的dp题了。$dp[i][j]$表示前$i$个数字,使用$j$个数字量化的最小误差。当前决策一定关于第$i$个数字有关,问题在于究竟这一段要多长。在枚举一个k,转移就很明显了:

    $dp[i][j]=Min{dp[k][j-1]+quantize(k+1,i)}$

    究竟用什么来做量化的值

    问题转化为了如何求$quantize$函数。就这道题的范围来看,从最小到最大枚举也是没有问题的。但实际上有$O(1)$方法。

    设我们选择$m$来量化,也就是说函数$y=sumlimits_{i=l}^{r}(m-a[i])^2$最小。问题转化为求此函数的最小值。

    这是个二次函数,所以展开后直接求导(或直接使用顶点公式),找到顶点的$x$坐标即可。

    $y=sumlimits_{i=l}^{r}(m-a[i])^2=(r-l+1)m^2-2msumlimits_{i=l}^{r}a[i]+sumlimits_{i=l}^{r}a[i]^2$

    当$y'=2(r-l+1)m-2sumlimits_{i=l}^{r}a[i]=0$时

    $m=frac{sumlimits_{i=l}^{r}a[i]}{r-l+1}$

    这就是平均值!由此我们得到结论:当利用平均值来当量化值时,差的平方之和最小。

    透过题解看本质

    其实对于这类最优化dp问题,可以看做是递归问题。dp的本质是记忆化搜索,只不过可以利用递推来实现。当我搜到这一步时需要枚举来决策当前需要多长的一段来量化,而剩余的则不需要递归,而是直接利用之前做好的最优子结构。这就是dp为什么比dfs快了。其实dp就是暴力,它终究需要考虑所有需要且可能的情况。

    my code

    具体在实现的时候通过统计前缀和以及平方的前缀和来$O(1)$完成求解。注意把$m$代入求误差时也要利用前缀和。

    注意平均值应该四舍五入。

    /*By DennyQi 2018*/
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 110;
    const int MAXM = 20010;
    const int INF = 0x3f3f3f3f;
    inline int Max(const int a, const int b){ return (a > b) ? a : b; }
    inline int Min(const int a, const int b){ return (a < b) ? a : b; }
    inline int read(){
        int x = 0; int w = 1; register char c = getchar();
        for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
        if(c == '-') w = -1, c = getchar();
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
    }
    int T,N,s;
    int a[MAXN],dp[MAXN][12],sum[MAXN],ssqr[MAXN];
    inline int calc(int l, int r, int m){
        int res = 0;
        res += m*m * (r-l+1);
        res += ssqr[r] - ssqr[l-1];
        res -= 2*m*(sum[r]-sum[l-1]);
        return res;
    }
    inline int quantize(int l, int r){
        int m = (sum[r]-sum[l-1]) / (r-l+1);
        return Min(calc(l,r,m), calc(l,r,m+1));
    }
    inline int Solve(){
        sort(a+1,a+N+1);
        sum[0] = ssqr[0] = 0;
        for(int i = 1; i <= N; ++i){
            sum[i] = sum[i-1] + a[i];
            ssqr[i] = ssqr[i-1] + a[i] * a[i];
        }
        memset(dp,INF,sizeof(dp));
        for(int i = 0; i <= s; ++i) dp[0][i] = 0;
        for(int i = 1; i <= N; ++i){
            for(int j = 1; j <= s; ++j){
                for(int k = 0; k < i; ++k){
                    dp[i][j] = Min(dp[i][j], dp[k][j-1] + quantize(k+1,i));
                }
            }
        }
        return dp[N][s];
    }
    int main(){
    //    freopen(".in","r",stdin);
        T = read();
        while(T--){
            N = read(), s = read();
            for(int i = 1; i <= N; ++i){
                a[i] = read();
            }
            printf("%d
    ", Solve());
        }
        return 0;
    }
  • 相关阅读:
    Hook钩子程序
    KMeans笔记 K值以及初始类簇中心点的选取
    自己用C#写的一个俄罗斯方块的小程序(附源代码)。
    那些帮助你成为优秀前端工程师的讲座——《JavaScript篇》
    Mac技巧合集第二期
    WCF增加UDP绑定(应用篇)
    第一个MVC4 Web应用程序
    jQuery的页面加载事件
    通过网页进行 iOS 应用内部分发
    sql count效率
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/10347533.html
Copyright © 2011-2022 走看看