首页 > 其他分享 >DP优化

DP优化

时间:2023-07-05 19:44:24浏览次数:37  
标签:哈夫曼 int LL 合并 long times 优化 DP

优化DP笔记

P6040 「ACOI2020」课后期末考试滑溜滑溜补习班

设 \(f_i\) 表示老师解决到第 \(i\) 个学生需要最少的精力,答案显然是 \(f_n\)

边界 : 对于 \(i=1\) , \(f_1=a_1\)

对于其他的 \(i\) 号学生,我们假设老师是从第 \(j\) 位学生过来的,所以状态转移方程分为三项

  1. \(f_j\) 表示之前的值
  2. \((i-j-1)\times d+k\) 表示应对调侃和移动花费的精力
  3. \(a_i\) 是解决当前学生问题花费的精力

整合起来方程就为

\[f_i=\min \{f_j+(i-j-1)\times d+k+a_i\} \]

当然 \(j\) 是有范围的 \(i-x≤j<i\) 当 \(j=i-1\) 时,实际上就是没有跳过

对于每个\(f_i\) ,只需要寻找最小的 \(f_j\) 转移过来就可以了,但这样要循环 \(i\) 和 \(j\) ,时间复杂度为 \(O(n^2)\)

然后对于 \([i-x,i-1]\) 的区间内寻找最小值,我们可以用单调队列可以维护求出来

把 \(f_j\) 提出来即可

\[f_i=a_i+k+(i-1)\times d+\min\{dp_j-j\times d\} \]

接下来我们维护 \(f_j-j \times d\)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7 + 10;
inline int read()
{
    int x = 0, f = 1;
    char s = getchar();
    while (s < '0' || s > '9')
    {
        if (s == '-')
            f = -f;
        s = getchar();
    }
    while (s >= '0' && s <= '9')
    {
        x = (x << 3) + (x << 1) + (s ^ 48);
        s = getchar();
    }
    return x * f;
}

LL n, k, d, x, tp, seed;
LL a[N], q[N], f[N];
inline LL rnd()
{
    static const int MOD = 1e9;
    return seed = (1LL) * (seed * 0x66CCFF % MOD + 20120712) % MOD;
}

int main()
{
    cin >> n >> k >> d >> x >> tp;
    if (tp)
    {
        seed = read();
        for (int i = 1; i <= n; i++)
            a[i] = rnd();
    }
    else
    {
        for (int i = 1; i <= n; i++)
            a[i] = read();
    }
    f[1] = a[1];
    int head = 1, tail = 1;
    q[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        while (head <= tail && q[head] < i - x)
            head++;
        f[i] = f[q[head]] + a[i] + k + (i - q[head] - 1) * d;
        while (head <= tail && f[q[tail]] - q[tail] * d >= f[i] - i * d)
            tail--;
        q[++tail] = i;
    }
    cout << f[n] << endl;
    return 0;
}

P2168 [NOI2015] 荷马史诗

这道题的本体为哈夫曼数。

以二叉哈夫曼树为例,给 \(n\) 个点,构造一棵哈夫曼树,每次选剩下的两棵权值最小的树合并成一棵新树,新树的根权值等于前两棵合并的树的根权值和

例如下图

例一

(a图):四个点 \(a,b,c,d\),权值分别为\(7,5,2,4\)

(b图):先把 \(2(c)\) 和 \(4(d)\) 合并,新的根节点值为 \(6\)

(c图):再把 \(5(b)\) 和 \(6(c+d)\) 合并,新的根节点为 \(11\)

(d图):最后把 \(7(a)\) 和 \(11(b+c+d)\) 合并,新的根节点为 \(18\)

那么这道题便是 \(k\) 叉哈夫曼树,但如果真正去写就会发现一种情况:

当最后一次合并之后,可能树上只剩下 \(m\) 个节点,且 \(1<m<k\) 无法再合并

所以我们可以用补点的方法这个方法:

首先,我们每次合并用掉 \(k\) 个值又合并出来一个值,那么按每次合并来算,我们每次用掉了 \(k-1\) 个值,然后,我们最终需要只剩 \(1\) 个值,也就是需要合并 \(n-1\) 个值,所以我们只要判断 这个东西\((n-1)\mod (k-1)=0\) 成立,我们还需要补 \((k-1)-((n-1)\mod (k-1)\) 个点

接下来我们看一个例子

原文为:\(AMCADEDDMCCAD\)

我们发现只有五个字母 \(E,M,C,A,D\) ,使用频率为 \(\{1,2,3,3,4\}\)

img

绿色为原来的点,白色为补点,\(0\),\(1\) 表示左右儿子

哈夫曼编码为

\(E=000,I=001,C=01,A=10,D=11\)

答案为树的所有叶子节点的带权路径长度之和与树的最大深度

因为Huffman每次合并都要排序,所以使用堆(直接用priority_queue更方便)。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

struct node
{
    LL w, h; //表示权值和高度
    bool operator<(const node &x) const
    {                 //重载运算符
        if (w != x.w) //优先考虑权值
            return w > x.w;
        return h > x.h;
    }
};
priority_queue<node> haff;

LL n, k, ans1, ans2, x; //单词数量、进制
//重新编码以后的最短长度、最长字符串s[i]的最短长度

int main()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
    {
        cin >> x;
        haff.push(node{x, 1}); //默认高度为1
    }
    if ((n - 1) % (k - 1)) //判断是否需要补点
    {
        x = k - 1 - (n - 1) % (k - 1); //要补的点数
        for (int i = 1; i <= x; i++)
            haff.push((node){0, 1}); //补点
    }
    LL sum, maxx;            //临时答案
    while (haff.size() != 1) //因为不可以是负数,所以大于1个点就跑
    {
        sum = 0, maxx = -2147483646;
        for (int i = 1; i <= k; i++) //每次选出k个数
        {
            sum += haff.top().w;            //加上队首
            maxx = max(haff.top().h, maxx); //找最小长度
            haff.pop();                     //队头出队
        }
        haff.push((node){sum, maxx + 1}); //加上合并的值
        ans1 += sum;
        ans2 = max(ans2, maxx);
    }
    cout << ans1 << endl << ans2;
    return 0;
}

「DPOI-1」道路规划

[USACO04DEC] Dividing the Path G

标签:哈夫曼,int,LL,合并,long,times,优化,DP
From: https://www.cnblogs.com/ljfyyds/p/17529634.html

相关文章

  • UDP 编程不能太随意
    UDP相比TCP虽然是是无连接的,看似发送接收都很随意,但是在发送——接收过程中,仍然有些问题需要重视。在整个通讯过程中至少有两点需要注意,一方面要防止发送方的一厢情愿,另一方面是在允许的条件下,尽量保证数据的完整性。防止发送方的一厢情愿是指在发送时要确保对方有套接字可以......
  • Wordpress:安装Astra主题后,无法找到主题模板?
    在使用Wordpress安装Astra后,发现侧栏Appearance没有出现StarterTemplates,这样就无法使用很多Astra相关的免费模板,如何解决?1.点击Plugins,在搜索框输入StarterTemplates,安装后激活 2.在Appearance找到StarterTemplates,进入即可选择喜欢的模板。 ......
  • 34最优化算法
    好的,以下是常见最优化算法的公式,使用Markdown格式进行展示:1.梯度下降法(GradientDescent):参数更新公式:\(\theta^{(t+1)}=\theta^{(t)}-\alpha\nablaJ(\theta^{(t)})\)2.随机梯度下降法(StochasticGradientDescent):参数更新公式:\(\theta^{(t+1)}=\theta^{(t)}-......
  • m基于细菌觅食优化的DSR网络路由协议优化算法matlab仿真
    1.算法仿真效果matlab2022a仿真结果如下:    2.算法涉及理论知识概要        移动自组网(MobileAdHocNetwork,简称MANET)是一种无需基础设施支持的网络,它由一组移动的节点组成,这些节点可以自组织形成一个网络,实现数据的传输和共享。由于MANET是一种去中心化......
  • SQL优化还凭经验?这个工具能帮你智能优化SQL
    前言SQL优化是程序开发中经常遇到的问题,尤其是在程序规模不断扩大的时候。SQL的好坏不仅制约着程序的规模,影响着用户的体验,甚至威胁着信息的安全。我们经常听到说哪家平台挂了,哪家网站被黑了,但我们不知道,其实这些平台挂了、被黑了的原因很多时候在于SQL不够健壮。SQL不够健壮......
  • Python基础37 基于tcp、udp套字编程、粘包现象、struct模块
    基于tcp协议的套接字编程(sochet编程)什么是socket?通常翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把tcp/ip层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中。套接字分类:AF_UNIX:用在局域网中AF_INET:用在互联网中客户端和服务端启动顺序......
  • 象群游牧优化算法(EHO)(Matlab完整代码实现)
    ......
  • centos8环境基本优化
    centos8环境基本优化目录centos8环境基本优化1.防火墙优化2.源优化:方案1.更换阿里源方案2.使用centos8.5源安装epel源3.ssh连接慢解决4.关闭公网,只开放内网(可选)5.配置定时任务,时间同步centos7ntpdcentos86.PS17.配置sudo1.防火墙优化参考链接https://www.cnblogs.com/ztton......
  • python基础day37 基于TCP、UDP协议的套接字编程和粘包现象
    基于TCP协议的套接字编程(socket编程)什么是Socket?我们经常把Socket翻译为套接字,Socket是在应用层和传输层之间的一个抽象层,它把TCO/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信  套接字的分类:AF_UNIX:用在局域网中AF_INET:用在互联网中客户......
  • 数仓性能调优:大宽表关联MERGE性能优化
    摘要:本文主要为大家讲解在数仓性能调优过程中,关于大宽表关联MERGE性能优化过程。本文分享自华为云社区《GaussDB(DWS)性能调优:大宽表关联MERGE性能优化》,作者:譡里个檔。【业务背景】如下MERGE语句执行耗时长达2034sMERGEINTOsdifin.hah_ae_line_sr_t_02_8663Event_1u18ol......