• 双“11”的抉择


    题目描述

    把钱花完了,所以单身了,单身了所以过双“11”,过双“11”所以把钱花完了。

    今年Nova君(三号)照旧过着他暗无天日的“买买买”的双“11”,然而因为囊中羞涩,并不能够太任性。他的购物车中,列满了数不清的商品,共有N件,好多商品居然还不止一件 __(:3 」∠)_ 现在Nova君要做出一个艰难的抉择,他要从所有商品中挑出m件拼成一个订单,请问有多少种凑单的方法呢?求访法数对M的余数。

    PS:同一种商品不作区分。

    输入

    多组测试数据(不超过100组)

    每组数据两行,第一行为三个正整数N,m,M,具体意义详见描述,第二行为N个正整数a1,a2,,,an,代表第i个商品的个数

    (1<=N,ai,m<=1000,2<=M<=10000)

    输出

    对于每组数据,输出一行,表示方法总数

    输入样例

    3 3 10000
    1 2 3

    输出样例

    6
    解题思路:

    关键词:多重集组合数

    1、为了拉不重复计算,最好是同一种物品一次性解决掉,所以定义如下:

    dp[i+1][j] : 从前 i+1 种物品中取出 j 个的方案总数

    2、状态转移方程:

    前 i+1 种物品取出 j 个 = 前 i+1 种物品取出 j-1个 + 前 i 种物品取出 j 个 - 前i种物品中取出 j-1-ai 个.

    因为取出 j-1-ai个, 最后需要 j-1个, 则 ai 需要全部取出, 前两个相重复, 则必然全部取出.

    递推公式: dp[ i+1 ][ j ] = dp[ i+1 ][ j-1 ] + dp[ i ][ j ] - dp[ i ][ j-1-ai ]     时间复杂度O(nm).

    呈上代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N=1010;
    int a[N];
    int b[N][N];
    int main()
    {
        int n,m,M;
        while(scanf("%d%d%d",&n,&m,&M)==3)
        {
            for(int i = 0; i < n; i++)
                scanf("%d",&a[i]);
    
            for(int i = 0; i <= n; i++)
                b[i][0] = 1;
    
            for(int i = 0; i < n; i++)
                for(int j = 1; j <= m; j++)
                {
                    if(j -1 - a[i] >= 0)
                        b[i+1][j] = (b[i][j] + b[i+1][j-1] - b[i][j-1-a[i]] +M)%M;
                    else
                        b[i+1][j] = (b[i][j] + b[i+1][j-1])%M;
                }
            printf("%d
    ",b[n][m]);
        }
    
    }
  • 相关阅读:
    PHP获取时间or戳?
    滤镜灰CSS
    css3 文字渐变色
    除指定区域外点击任何地方隐藏DIV
    margin-top bug 处理方案
    基于Bootstrap好用的瀑布流
    初始数据库
    协程
    粘包及解决方案
    log日志的三种方式
  • 原文地址:https://www.cnblogs.com/zpfbuaa/p/4978945.html
Copyright © 2020-2023  润新知