首页 > 其他分享 >P3188 做题笔记

P3188 做题笔记

时间:2024-07-28 14:20:52浏览次数:13  
标签:le int max P3188 笔记 kN ++ kV

题目

HN 省选作恶多端

观察

拿到题面,定睛一看:欸,这不是裸的 01 背包吗。但是这是道紫题,还是在省选的赛场上,应该有蹊跷。再一看到数据范围

\(1 \le W, w_i, v_i \le 2^{30}\)

这么大,是人能做的吗?

观察题目,注意到

保证 \(w_i = a * 2^b\),且 \(a \le 10, b \le 30\)

前半句不重要,重要的是 \(a \le 10\),这启示我们可以在 a 上面下文章

正解

我们对每个不同的 b 分别考虑,对于每一组 b,可以以 a 为重量进行 01 背包

设 \(f_{i, j}\) 表示在取 \(j * 2^i\) 重量下,最大的价值是多少。根据定义和 01 背包相关知识,我们可以初步推出转移式:

\[f_{i, j} = \max(f_{i, j}, f_{i, j - w_k} + v_k) \]

其中

  • \(w_k\) 表示第 i 组中的所有重量
  • \(v_k\) 表示第 i 组中的所有价值

接下来考虑不同组的转移。

考虑 \(f_{i, j}\) 可以由什么转移过来

  • 上一组 i 中所有 \(k \le j\) 的 \(f_{i - 1, k}\)

  • 若 W 第 i 位上为 1,则这一组中重量可以多选 \(2^i\),所以还可以从 \(\max\limits_{k \le k' \le k + 2^i} f_{i, k'}\) 转移过来,又因为 \(f\) 数组单调不减,所以可以直接从 \(f_{i, k + 2^i}\) 转移过来

综上,可以写出转移式:

\[f_{i, j} = max(f_{i, j}, f_{i - 1, k + W_i} + f_{i, j - k}) \]

则最后的答案就是 \(f_{\log_2 W, 1}\)

Code
#include <cmath>
#include <iostream>
#include <vector>

using namespace std;
using pii = pair<int, int>;

const int kN = 101, kV = 1001;

int n, W, f[kN][kV];
vector<pii> v[kN];

void I() {
  fill(f[0], f[kN - 1] + kV, 0);
  for (int i = 0; i < kN; i++) {
    v[i].clear();
  }
}

void S() {
  for (int i = 1, w, v, s; i <= n; i++) {
    cin >> w >> v;
    for (s = 0; w % 2 == 0; w >>= 1, s++) {
    }
    ::v[s].push_back({w, v});
  }
  for (int l = 0; l < kN; l++) {
    for (int i = 0; i < v[l].size(); i++) {
      for (int j = kV - 1; j >= 0; j--) {
        if (j - v[l][i].first >= 0) {
          f[l][j] = max(f[l][j], f[l][j - v[l][i].first] + v[l][i].second);
        }
      }
    }
  }
  for (int l = 1; l < kN; l++) {
    for (int j = kV - 1; j >= 0; j--) {
      for (int k = 0; k <= j; k++) {
        f[l][j] = max(f[l][j], f[l - 1][min(2 * k + ((W >> l - 1) & 1), kV - 1)] + f[l][j - k]);
      }
    }
  }
  cout << f[(int)log2(W)][1] << '\n';
}

int main() {
  cin.tie(0)->sync_with_stdio(0);
  while (cin >> n >> W) {
    if (n == -1) {
      break;
    }
    I();
    S();
  }
  return 0;
}

标签:le,int,max,P3188,笔记,kN,++,kV
From: https://www.cnblogs.com/SuperSupper/p/18328175

相关文章

  • MSPM0G3507外设DMA学习笔记
    概述变量的存储正常情况下,变量存储在SRAM中,如果要发送该变量的值到外设,需要调用内核操作,使SRAM中的数据送到外设。此类型操作过多会导致占用CPU高,整体卡顿。DMA控制概述DMA:DirectMemoryAccess专门用于数据传输,解放CPU对于DMA,CPU首先启动传输,然后在传输过程中执行其......
  • C语言笔记
    各位同好,作为一名C语言学习小白,在经过了一个学期的学习后我拿到了满分的期末成绩,现分享给大家自认为宝贵的笔记。开始写一些C语言的笔记取位数个位:n%10十位:n/10%10百位:n/100%10冒泡排序for(i=1;i<=n-1;i++)     for(j=0;j<n-i;j++)     ......
  • 组合数学学习笔记(一)(2024.7.3)
    一、组合数1.递推式$\displaystyle\binom{n}{m}=\displaystyle\binom{n-1}{m-1}+\displaystyle\binom{n-1}{m}$证:左边相当于从$n$个数中选$m$个数,右边枚举第$n$个数选不选。如果选,就从剩下$n-1$个数中选$m-1$个;如果不选,就从剩下$n-1$个数中选$m$个。2.对称性......
  • 树分治、动态树分治学习笔记
    点分治点分治适合处理大规模的树上路径信息问题,选取重心,将当前的树拆分为几颗子树,然后递归子树求解问题,但是今天的重点不在这里边分治与点分治类似,选取一条边,均匀地将树分成两个部分,但是对于一个点有多个儿子时,时间复杂度就会非常大,于是我们可以将其转化,这里有两种方法\(1.\)......
  • Datawhale AI夏令营 第三期Task1 笔记
    逻辑推理赛道baseline代码分析与总结前言主要是对baseline的代码进行了代码分析和流程总结,以及个人的一点关于prompt的想法目录引入依赖包设置模型和API密钥API调用和重试机制生成Prompt和解析结果处理数据主函数评估和过滤辅助函数深度学习知识点总结1引入依赖包首......
  • 从 jupyter 笔记本连接到容器化 postgresql
    我有一个运行postgresql的容器,我想摄取一些我在ETL脚本中准备好的数据。我已经创建了数据库,但是,当我尝试通过jupyter笔记本连接本地计算机时,它一直说找不到主机,即使我在.yaml文件中设置了它。我的.yaml文件是这样的:version:'3.7'services:postgres:image:......
  • 模拟退火学习笔记
    模拟退火学习笔记前言不知道为啥突然有闲情学这个...模拟退火(SimulatedAnnealing),简称\(SA\).是一种基于随机化的算法,无门槛,主要是为了骗分...不是正解!!!!根据爬山算法的过程,我们发现:对于一个当前最优解附近的非最优解,爬山算法直接舍去了这个解。而很多情况下,我们需......
  • Python学习笔记46:游戏篇之外星人入侵(七)
    前言到目前为止,我们已经完成了游戏窗口的创建,飞船的加载,飞船的移动,发射子弹等功能。很高兴的说一声,基础的游戏功能已经完成一半了,再过几天我们就可以尝试驾驶飞船击毁外星人了。当然,计分,游戏次数,背景音乐,开始启动等按钮的功能需要我们慢慢添加,这些功能不影响游戏的使用,影......