「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)\}$

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

究竟用什么来做量化的值

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

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

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

$y=\sum\limits_{i=l}^{r}(m-a[i])^2=(r-l+1)m^2-2m\sum\limits_{i=l}^{r}a[i]+\sum\limits_{i=l}^{r}a[i]^2$

当$y‘=2(r-l+1)m-2\sum\limits_{i=l}^{r}a[i]=0$时

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

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

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\n", Solve());
    }
    return 0;
}

原文地址:https://www.cnblogs.com/qixingzhi/p/10347533.html

时间: 2024-08-02 22:40:16

「Algospot」QUANTIZE的相关文章

「Algospot」龙曲线DRAGON

一道考验思维的好题,顺便总结求第k大问题的常规思路: 传送门:$>here<$ 题意 给出初始串FX,每分形一次所有X替换为X+YF,所有Y替换为FX-Y.问$n$代字符串第$p$位起长度为$l$的串. 数据范围:$n \leq 50, p \leq 10^9, l \leq 50$ Solution 将求解一个串转化为求解第$k$个字符.这样的话只有求解$l$次字符就好了. 如果直接暴力去做,肯定从初始串开始暴力去一轮一轮的展开.而实际上并不需要展开每一个,因为只需要求一个字符.我只需要知道

「Algospot」津巴布韦ZIMBABWE

同时考验对状压DP和数位DP的理解: 传送门:$>here<$ 题意 给出一个数字$e$,现在对$e$通过$m$进行变换得到$x$:变换的要求是:1.只能改变原数字$e$各个数位的顺序(可以有前导零) 2.$x$是$m$的倍数 3. $x<e$ 问$x$有几种取值满足? 数据范围:$e \leq 10^14, m \leq 20$ Solution DFS怎么做是关键 这道题如果使用DFS,那么大体思路就是枚举每一位填放什么数字. 那么需要一个数组(状压)在此过程中来记录还可以用哪些数字

AC日记——「HNOI2017」单旋 LiBreOJ 2018

#2018. 「HNOI2017」单旋 思路: set+线段树: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #define maxtree maxn<<2 int val[maxtree],tag[maxtree],L[maxtree],R[maxtree],mid[maxtree]; int op[maxn],ki[maxn],bi[maxn],cnt,size,n,ch[maxn]

「随笔」基于当下的思考

马德,说好的技术blog,变成日记本了... 下午的时候莫名其妙的感到很颓废,因为自己的不够强大感到忧虑和危机感十足.现在每每行走在技术的道路上,常觉得如履薄冰,如芒在背. 上大学之前和现在的心态其实差别挺大的,视野的开阔远远不止局限于自己的脚下.不过,这里的「上大学之前」只是一个时间描述词,并不觉得大学是最适合学习的地方,我很失望. 世界上的人无论性别,区域,宗教,兴趣爱好,总可以在互联网上找到志趣相同的人,总是可以不断打破自己的常识与惯性思维.总是有在相同领域比自己更强的人,挺好的. 关于知

「Unity」与iOS、Android平台的整合:3、导出的Android-Studio工程

本文属于「Unity与iOS.Android平台的整合」系列文章之一,转载请注明出处. Unity默认导出的是Android-Eclipse工程,毕竟Eclipse for Android开发在近一两年才开始没落,用户量还是非常巨大的. 个人认为AndroidStudio非常好用,能轻易解决很多Eclipse解决不了或者很难解决的问题. 所以我将Unity导出的Andoid工程分为Eclipse和AndroidStudio两部分. 不过我之后的相关内容都会使用AndroidStudio,希望依然

大数据和「数据挖掘」是何关系?---来自知乎

知乎用户,互联网 244 人赞同 在我读数据挖掘方向研究生的时候:如果要描述数据量非常大,我们用Massive Data(海量数据)如果要描述数据非常多样,我们用Heterogeneous Data(异构数据)如果要描述数据既多样,又量大,我们用Massive Heterogeneous Data(海量异构数据)--如果要申请基金忽悠一笔钱,我们用Big Data(大数据) 编辑于 2014-02-2817 条评论感谢 收藏没有帮助举报作者保留权利 刘知远,NLPer 4 人赞同 我觉得 大数据

开放的智力8:实用「成功学」

可实现的「成功学」 现在我想为这里的年轻人介绍一种可实现的「成功学」.希望这个我自创的理论,可以改变很多人的一生. 当我们评价一个事情值不值得去做.应该花多少精力去做的时候,应该抛弃单一的视角,而是分两个不同的维度来看,一是该事件将给我带来的收益大小(认知.情感.物质.身体方面的收益皆可计入),即「收益值」:二是该收益随时间衰减的速度,我称为「收益半衰期」,半衰期长的事件,对我们的影响会持续得较久较长. 这两个维度正交以后就形成了一个四象限图.我们生活.学习和工作中的所有事情都可以放进这个图里面

Linux 小知识翻译 - 「syslog」

这次聊聊「syslog」. 上次聊了「日志」(lgo).这次说起syslog,一看到log(日志)就明白是怎么回事了.syslog是获取系统日志的工具. 很多UINIX系的OS都采用了这个程序,它承担了「获取系统全部的日志」这个维持系统正常运行的重要任务. syslog的本体是「syslogd」这个daemon(一般翻译成守护进程),常驻内存中获取日志. syslog的特点是可以通过配置文件「/etc/syslog.conf」,对「哪种应用程序?哪种重要度的信息?记录在哪个文件中?」等进行细致的

Linux 小知识翻译 - 「日志」(log)

这次聊聊「日志」. 「日志」主要指系统或者软件留下的「记录」.出自表示「航海日志」的「logbook」. 经常听说「出现问题的时候,或者程序没有安装自己预期的来运行的时候,请看看日志!」. 确实,记录了系统和软件详细运行情况的「日志」是信息的宝库,通过日志来解决问题的事例也非常多. 但事实上,「无论如何也不会看日志」的用户也有很多.理由很简单,日志的信息量非常大,全部用眼睛来看的话是非常吃力的. 而且,英语写的日志也会让英文不好的人敬而远之. 虽说「要养成用眼睛来看日志的习惯」,但实行起来却非常