POJ3046 多重集组合数 dp+前缀和优化+滚动数组 (包含类似优化的小总结)

原创 2015年10月27日 12:09:48

题意

  • T种数,每种有a[t]个,总共有A个数。问你取其中X个数作为子集,有多少种这样的子集。计算L<=X<=H的子集数之和,结果mod 1000000
  • 1<=T<=1000, 1<=a[t] <= 100,即1 <= A <= 100000, L<=H<=A

思路

  • 基本想法:可以类比为把求max改为求sum的多重背包,所以基本的递推关系很好写,dp(i,j) = sum dp(i-1)(j-k) 其中k = 0~a[i] ,dp(i,j)表示前i种数,其中j个数作为子集的取法。
  • 很明显,这样做会超时、超空间,超空间好说,是基本的滚动数组。我们主要解决超时的问题。
  • 前缀和优化,我们把状态改一下。dp(i,j)表示前i种数,其中0~j个数作为子集时的取法。这样dp(i,j) = dp(i-1, j) - dp(i-1, j-a[i]-1) + dp(i,j-1) 时间复杂度的问题就解决了。
  • 滚动数组,我们可以看到,每次更新i时,我们要用到i-1时的 j 和 j-a[i]-1,那么应该倒着更新j。可是这样的话,在更新dp(i,j)时,dp(i,j-1)还没有更新出来,无法更新dp(i,j)。这里我们有两种解决方案,一是,保存两个dp(j),这样我们通过用i&1找到用哪个dp,然后正向更新dp(j),另一种是,保存一个dp(j),做两次循环更新它。第一次做,dp(i,j) = dp(i-1, j) - dp(i-1, j-a[i]-1),求出不带前缀和的,第二次专门求前缀和。

一些小总结

  • 这种前缀和优化适用于每次求sum时,不用再乘一个和j相关的系数时,比如dp (i,j) = sum dp(i-1)(j-k) * c(j) 这时就无法用前缀和来优化了。
  • 滚动数组的第二种方案是我这次突然想到的,我觉得也挺好的,把问题差分了,相当于加入了一个辅助的数组,一个保存前i种数,其中j个数作为子集的取法,另一个保存前i种数,其中0~j个数作为子集时的取法,然后再更新时比较好写。
  • 另外,我想不是求sum的问题时,比如max和min也都可以用类似的想法优化。也是当没有更多相关的系数时,我们可以用RMQ问题或者线段树作为一个辅助数组,来优化时间复杂度。

实现

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

int T,n,L,H;
const int mod = 1000000;
int dp[100005];
int a[1005];

int main(){
    int i,j;
    cin>>T>>n>>L>>H;
    for (int i=0;i<n;i++){
        int tmp;
        scanf("%d",&tmp);
        a[tmp]++;
    }
    for (int i=0;i<=H;i++)
        dp[i] = 1;
    for (int i=1;i<=T;i++){
        for (int j=H;j>=0;j--){
            if (j > a[i]){
                dp[j] = (dp[j] - dp[j-a[i]-1] + mod) % mod;
            }
        }
        for (int j=1;j<=H;j++){
            dp[j] = (dp[j] + dp[j-1]) % mod;
        }
    }
    cout << (dp[H] - dp[L-1] + mod) % mod<< '\n';

    return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luke2834/article/details/49446545

前缀和问题

一维前缀和这个优化主要是用来在O(1)时间内求出一个序列a中,a[i]+a[i+1]+……+a[j]的和。具体原理十分简单:用sum[i]表示(a[1]+a[2]+……+a[i]),其中sum[0]=...
  • K_rew
  • K_rew
  • 2016-01-16 11:18:53
  • 5733

codeforces 708E——前缀和优化dp

题目大意为: 一个n*m块砖的建筑,一共k天,每天风从两边吹,吹掉砖的概率为p,反之为1-p求最终建筑没有倒塌的可能性(上层与下层有交集且每一层都有砖) 1 先预处理出pl[],pr[](k天后...
  • F_Darcy
  • F_Darcy
  • 2016-09-24 00:03:49
  • 399

前缀和优化

在了解之前,先来阐明几个基础概念: 1.时间复杂度O(n):时间复杂度在算法中是一个重要概念,此处O为微积分概念,不做详解。我们可以简记一重循化时间复杂度即为O(n),二重循环时间复杂...
  • qq_38973846
  • qq_38973846
  • 2017-11-19 10:51:10
  • 241

CDOJ 1307 ABCDE dp, 前缀和优化

题目链接:http://acm.uestc.edu.cn/#/problem/show/1307 题意: 在数电中,有一种码,类似BCD码这种玩意儿第i位如果为1的话,那么ans+=a[i],a[...
  • just_sort
  • just_sort
  • 2017-03-23 21:18:52
  • 640

poj3666(dp前缀优化)

链接:点击打开链接 题意:将A1....An变为B1.....Bn,要求序列B满足非严格单调递增或非严格单调递减,代价为|A1-B1|+|A2-B2|+...+|AN -BN|,输出最小代价 代码: ...
  • stay_accept
  • stay_accept
  • 2016-05-31 17:35:23
  • 585

(经典)POJ-3046 多重集组合数

题目大意:蚂蚁牙黑,蚂蚁牙红:有A只蚂蚁,来自T个家族,分别记为ant[i]个。同一个家族的蚂蚁长得一样,但是不同家族的蚂蚁牙齿颜色不同。任取n只蚂蚁(S 题目链接:点击打开链接 分析: 多...
  • AC_hell
  • AC_hell
  • 2016-05-14 14:23:41
  • 716

poj3046(递推)

链接:点击打开链接 题意:有A个数,一共T种,从中选一些数作为一个集合,问集合大小在A到B中的种类有多少 代码:...
  • stay_accept
  • stay_accept
  • 2015-11-30 21:04:56
  • 500

POJ 3046 Ant Counting 简单DP

题意也比较简单了。  大概是: 给出T种数字。每种各有N[i]个 然后用这些数字构成一些序列, 问x长度到y长度的序列有多少种 那么就是DP了 dp[i][j] 表示前i种数字构成长...
  • sdj222555
  • sdj222555
  • 2013-08-28 13:05:43
  • 3097

POJ3046 Ant Counting DP 隔板法讲解 有图有真相

POJ 3046  Ant Counting 描述 Description Bessie was poking around the ant hill one day watching the ant...
  • moep0
  • moep0
  • 2016-11-02 15:28:43
  • 373

Poj 3046(dp)

Ant Counting Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 1954   A...
  • Wiking__acm
  • Wiking__acm
  • 2013-08-20 16:23:03
  • 1579
收藏助手
不良信息举报
您举报文章:POJ3046 多重集组合数 dp+前缀和优化+滚动数组 (包含类似优化的小总结)
举报原因:
原因补充:

(最多只允许输入30个字)