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;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

poj3666(dp前缀优化)

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

写给程序员的!(不要浮躁)------以此来激励自己

1.不要看到别人的回复第一句话就说:给个代码吧!你应该想想为什么。当你自己想出来再参考别人的提示,你就知道自己和别人思路的差异。 2.初学者请不要看太多太多的书那会误人子弟的,先找本系统的学,很多人...
  • johnny710vip
  • johnny710vip
  • 2011年08月12日 09:57
  • 3227

codeforces 708E——前缀和优化dp

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

前缀和问题

一维前缀和这个优化主要是用来在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
  • 4168

poj3666(dp前缀优化)

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

数据库性能优化1——正确建立索引以及最左前缀原则

1. 索引建立的原则 用于索引的最好的备选数据列是那些出现在WHERE子句、join子句、ORDER BY或GROUP BY子句中的列。 仅仅出现在SELECT关键字后面的输出数据列列表中的数据列...
  • zhanglu0223
  • zhanglu0223
  • 2013年03月24日 16:58
  • 17290

矩阵乘法优化DP

矩阵乘法优化DP 在许多的DP题目中,转移方程本身不难推,但是需要循环的次数巨大。这时候可以利用矩阵乘法将时间复杂度从O(n) 优化到 O(log n)。这里只用十分简单的一维DP做例子。如何乘在矩...
  • u011056504
  • u011056504
  • 2016年06月04日 17:03
  • 1564

HDU - 5669 Road 线段树优化建边+分层图最短路

题目链接点这里 线段树优化建边太厉害了,, 这题题解说的很清楚了:点这里 不过实现上有很多小技巧,,强烈建议做一下 #include #include #include #include #i...
  • qq_30927651
  • qq_30927651
  • 2017年06月17日 07:32
  • 186

HDU 5550 dp + 前缀和优化

题意:有个大楼有n层,每层都有两种人,一种喜欢的打球,一种喜欢游泳,现在要你在每一层要么开设球馆,要么开设游泳馆,分配完毕以后,打球的要到球馆,游泳的要到游泳馆,问最优分配下,所有人要移动的最短距离和...
  • Bahuia
  • Bahuia
  • 2016年11月06日 00:14
  • 180

codeforces 708E——前缀和优化dp

题目大意为: 一个n*m块砖的建筑,一共k天,每天风从两边吹,吹掉砖的概率为p,反之为1-p求最终建筑没有倒塌的可能性(上层与下层有交集且每一层都有砖) 1 先预处理出pl[],pr[](k天后...
  • F_Darcy
  • F_Darcy
  • 2016年09月24日 00:03
  • 316
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:POJ3046 多重集组合数 dp+前缀和优化+滚动数组 (包含类似优化的小总结)
举报原因:
原因补充:

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