POJ 3046 Ant Counting(多重集组合数,归纳优化第三层循环)

http://poj.org/problem?id=3046
就是这种模型:
N N N 种物品,每种有 a i a_i ai 个,从中选出 m m m 个,求方案数?
参考博客:https://blog.csdn.net/ac_hell/article/details/51405598
还是直接来样例比较直接:
样例就是:有 两个 1 1 1,两个 2 2 2 ,一个 3 3 3,问从中选 2 2 2 个数 加上 从中选 3 3 3 个数的方案数之和
d p [ i ] [ j ] dp[i][j] dp[i][j] 表示从前 i i i 种物品中,选出 j j j 个的方案数
那就是求 d p [ 3 ] [ 2 ] + d p [ 3 ] [ 3 ] dp[3][2]+dp[3][3] dp[3][2]+dp[3][3]

d p [ 3 ] [ 2 ] dp[3][2] dp[3][2]
就是当前在处理第三种物品
假如这次第三种物品一个都不拿,那么方案数就是 d p [ 2 ] [ 3 − 0 ] dp[2][3-0] dp[2][30]
假如这次第三种物品拿 1 1 1 个,那么方案数就是 d p [ 2 ] [ 3 − 1 ] dp[2][3-1] dp[2][31]
假如这次第三种物品拿 2 2 2 个,那么方案数就是 d p [ 2 ] [ 3 − 2 ] dp[2][3-2] dp[2][32]
依次类推。。。。
所以方程就出来了:

d p [ i ] [ j ] = d p [ i − 1 ] [ j − 0 ] + dp[i][j]=dp[i-1][j-0]+ dp[i][j]=dp[i1][j0]+ d [ i − 1 ] [ j − 1 ] + d p [ i − 1 ] [ j − 2 ] + . . . . . + d p [ i − 1 ] [ j − j ] d[i-1][j-1]+dp[i-1][j-2]+.....+dp[i-1][j-j] d[i1][j1]+dp[i1][j2]+.....+dp[i1][jj]

但是这道题的妙点在于优化(虽然好像不优化也能过)

优化

而后面这一坨红色的是不是相当于 从拿 1 1 1 个,拿 2 2 2 个。。。。直到拿完,
而我们的 d p dp dp 是从拿 0 0 0 个开始,直到拿完,是不是感觉只有一点点的差别

那我们的 j j j 比原来少拿一个,变成 j − 1 j-1 j1,不是就相当于从 0 0 0 个开始拿了么~~~
所以后面这一坨红色的其实是 d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j1]

那么方程就变成了: d p [ i ] [ j ] = d p [ i − 1 ] [ j − 0 ] + dp[i][j]=dp[i-1][j-0]+ dp[i][j]=dp[i1][j0]+ d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j1]

这个方程就是主要的思路,然后就是一些细节问题了,比如我们这里的 d p [ 3 ] [ 2 ] dp[3][2] dp[3][2],第 3 3 3 个物品最多才 1 1 1 个,怎么能拿 2 2 2 个哇,
所以当当前这一种个数不够的时候就要减去那么多,就变成:
d p [ i ] [ j ] = d p [ i − 1 ] [ j − 0 ] + dp[i][j]=dp[i-1][j-0]+ dp[i][j]=dp[i1][j0]+ d p [ i ] [ j − 1 ] − d p [ i − 1 ] [ j − 1 − c ] dp[i][j-1]-dp[i-1][j-1-c] dp[i][j1]dp[i1][j1c]

诶不对啊,后面多的不一定只有 j-1-c 这一个啊,万一还多了 j-2-c,j-3-c,这些怎么办喃?为啥就只减去这一项?还是有点没想通

#include"iostream"
#include"string.h"
using namespace std;
const int MOD=1e6;
const int maxn=1e3+5;
int dp[2][maxn*maxn];
int a[maxn];
int main()
{
    int N,ge,qL,qR;
    while(cin>>N>>ge>>qL>>qR)
    {
        memset(a,0,sizeof(a));
        for(int i=1;i<=ge;i++)
        {
            int t;
            cin>>t;
            a[t]++;
        }
        int pre=0,now=0;
        memset(dp,0,sizeof(dp));
        dp[0][0]=dp[1][0]=1;
        int M=0;
        for(int i=1;i<=N;i++)
        {
            now=pre^1;
            M+=a[i];
            for(int j=1;j<=M;j++)
            {
                if(j<=a[i])dp[now][j]=dp[pre][j]+dp[now][j-1];
                else dp[now][j]=dp[pre][j]+dp[now][j-1]-dp[pre][j-1-a[i]];
                dp[now][j]=(dp[now][j]%MOD+MOD)%MOD;
            }
            pre=now;
        }
        long long ans=0;
        for(int i=qL;i<=qR;i++)
        {
            ans+=dp[now][i];
            if(ans>MOD)ans%=MOD;
        }
        cout<<ans<<endl;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值