HDU 1171 Big Event in HDU (多重背包+单调队列优化)

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1171


题目大意

有 n 种物品,每种物品有一个大小和数量。要求将所有的物品分成两部分,使两部分的总大小尽量接近。


题目分析

多重背包果题。
令 sum 为所有物品的大小总和。那么就是用给定的物品做多重背包,背包容量为sum/2,得到的结果是较小的一部分的大小。然后多重背包问题可以使用单调队列优化,O(nm) 。
这是我的多重背包+单调队列第一题。纪念一下。

对了,顺便说一下优化方法【以下引自kxy课件原话】

关于多重背包的优化
从简单的出发:
dp[ i ][ j ]=到第 i 个物品,选了重量不超过 j 的最大价值
dp[ i ][ j ]=max { dp[ i-1 ][ j-k*w[ i ] ]+k*v[ i ] } –条件略
我们可以观察到,根据 j mod w[ i ] 的值不同,可以分为w[i] 组,( 每一组的情况都是一样的 )
这是显然的,不用证明

我们先考虑 j mod w[ i ] =0 的情况
定义:
a[ j ]=dp[ i ][ j * w[ i ] ]
那么 dp[ i ][ (j+k)*w[ i ] ]=max { a[ j ]+k*v[ i ] , a[ j+1 ]+(k-1)*v[ i ] , …… , a[ j+k ] }
似乎发现了什么??

定义 b[ j ]=a[ j ]-j*v[ i ]
那么 dp[ i ][ (j+k)*w[ i ] ]=max { b[ j ] , b[ j+1 ] , …… , b[ j+k ] }+( j+k )*v[ i ]

dp[ i ][ (j+k)*w[ i ] ]=max { b[ j ] , b[ j+1 ] , …… , b[ j+k ] }+( j+k )*v[ i ]
求b[ j~j+k ]的最大值,用队列优化就行了

引用完毕,orz
下面贴代码(模板,对了kxy模板i从0到n-1是过不了n=1的情况的,i必须做到n,实践证明)


DIE 马

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 1010
#define M 300000

using namespace std;


int n, m, sum;
int dp[M], q[M], qv[M], num[N], w[N];//dp数组滚动使用,q[]双端队列,下标, qv[]双端队列,保存值
int head, tail;


int main(){

    while(~ scanf("%d", &n) && n >= 0){

      sum = 0;

      memset(q, 0, sizeof(q));//记得清空
      memset(qv, 0, sizeof(qv));
      memset(dp, 0, sizeof(dp));

      for(int i = 1; i <= n; i++){
        scanf("%d%d", &w[i], &num[i]);
        sum += w[i] * num[i];
      }
      m = sum / 2;
      for(int i = 0; i <= n; i++){
        for(int a = 0; a < w[i]; a++){
          head = 0;  tail = -1;
          for(int j = 0; j * w[i] + a <= m; j++){
            int val = dp[j * w[i] + a] - j * w[i];
            while(head <= tail && qv[tail] <= val)  tail --;
            q[++tail] = j;  qv[tail] = val;//加入j
            dp[j * w[i] + a] = qv[head] + j * w[i];
            if(q[head] == j - num[i])  head ++;//退掉无用的值 
          }
        }
      }

      printf("%d %d\n", sum - dp[m], dp[m]);
    }

    return 0;
}

我和谁都不争,
和谁争我都不屑;
我爱大自然,
其次就是艺术;
我双手烤着,
生命之火取暖;
火萎了,
我也准备走了.
——[英]兰德(杨绛 译)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值