• 消失之物,分治


    ftiasch 有 N 个物品, 体积分别是 W1, W2, ..., WN。 由于她的疏忽, 第 i 个物品丢失了。 “要使用剩下的 N - 1 物品装满容积为 x 的背包,有几种方法呢?” -- 这是经典的问题了。她把答案记为 Count(i, x) ,想要得到所有1 <= i <= N, 1 <= x <= M的 Count(i, x) 表格。

    imageView Code

    输入格式

    第1行:两个整数 N (1 ≤ N ≤ 2 × 103) 和 M (1 ≤ M ≤ 2 × 103),物品的数量和最大的容积。

    第2行: N 个整数 W1, W2, ..., WN, 物品的体积。

    输出格式

    一个 N × M 的矩阵, Count(i, x)的末位数字

    样例

    样例输入

    3 2
    1 1 2
    

    样例输出

    11
    11
    21
    

    。。。我也不知道我是怎么想到的,只记得一开始我打了个O(n2mlogn)的代码交上后比暴力还慢。。。。

    由此想到:合并背包的复杂度为n2,所以与其合并背包还不如一个一个地添加元素,由此可以想到:我们

    考虑每个物品对复杂度的贡献,期望为mlogn,那么是不是可以用分治?so:假如我们有一棵线段树,在

    线段树的每一层对每一个物品跑背包,保证每个物品的贡献,so:我们还是考虑线段树:在线段树的

    每一个结点开一个背包,怎么用呢?考虑每个物品对答案的贡献,对线段树的没个非叶子节点:

    假设某个节点控制范围为l到r,则mid=l+r>>1;(废话)那么(l,mid)范围内所有的点会对(mid+1,r)

    的答案作出贡献,所以把(l,mid)内每个物品单独取出,加入到rc的背包中,并且在考虑以rc为根的子树时

    把rc的背包下传以传递贡献,下传时直接memcpy即可;在叶子节点统计答案,直接输出

     1 #include <bits/stdc++.h>
     2 #define N 2003
     3 #define reg register
     4 using namespace std;
     5 int n, m;
     6 int w[N];
     7 int pos[N];
     8 int f[N << 2][N];
     9 void ask(const int g, const int l, const int r) {
    10     if (l == r) {
    11         pos[l] = g;
    12         return;
    13     }
    14     const int mid = l + r >> 1, lc = g << 1, rc = g << 1 | 1;
    15     f[lc][0] = f[rc][0] = 1;
    16     for (int i = 1; i <= m; ++i) f[lc][i] = f[rc][i] = f[g][i];
    17     for (reg int i = mid + 1; i <= r; ++i) {
    18         for (reg int j = m; j >= w[i]; --j) {
    19             f[lc][j] += f[lc][j - w[i]];
    20             f[lc][j] = f[lc][j] >= 10 ? f[lc][j] - 10 : f[lc][j];
    21         }
    22     }
    23     for (reg int i = l; i <= mid; ++i) {
    24         for (reg int j = m; j >= w[i]; --j) {
    25             f[rc][j] += f[rc][j - w[i]];
    26             f[rc][j] = f[rc][j] >= 10 ? f[rc][j] - 10 : f[rc][j];
    27         }
    28     }
    29     ask(lc, l, mid);
    30     ask(rc, mid + 1, r);
    31 }
    32 int main() {
    33     scanf("%d%d", &n, &m);
    34     for (reg int i = 1; i <= n; ++i) scanf("%d", &w[i]);
    35     ask(1, 1, n);
    36     for (reg int i = 1; i <= n; ++i) {
    37         for (reg int j = 1; j <= m; ++j) putchar(f[pos[i]][j] + 48);
    38         puts("");
    39     }
    40 }
    感谢loj码风优化(貌似某位压行大神也写过?)

    优化:

    1.其实不必在所有节点开背包,在每层开一个即可;

    2.到达叶子节点可直接输出答案,不必最后统计输出;

  • 相关阅读:
    时间控件的操作
    Appium环境搭建
    测试常用英文词汇
    自动化测试中一段代码对应多个用例
    Linux常见命令
    元素的操作的简谈
    eclipse代码编辑界面代码块收缩的实现
    关键字驱动和数据驱动
    uni-app中如何判断浏览器内核
    JS实现 图片放大镜功能
  • 原文地址:https://www.cnblogs.com/loadingkkk/p/11272338.html
Copyright © 2020-2023  润新知