Cpp环境【NOIP2012普及组】【Vijos1792】摆花

【问题描述】   

  小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共 m 盆。通过调查顾客的喜好,小明列出了顾客最喜欢的n 种花,从1 到n 标号。为了在门口展出更多种花,规定第i 种花不能超过a i 盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。
试编程计算,一共有多少种不同的摆花方案。

  【输入格式】   

  第一行包含两个正整数 n 和 m,中间用一个空格隔开。
  第二行有 n 个整数,每两个整数之间用一个空格隔开,依次表示 a1、a 2、…、a n 。

 【输出格式】   

  输出只有一行,一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对 1000007 取模的结果。

 【输入样例】 

2 4
3 2

【输出样例】

2

【样例解释】 

  有2种摆花的方案,分别是(1,1 ,1 ,2),(1,1 ,2 ,2)。括号里的1和2表示两种花,比如第一个方案是前三个位置摆第一种花,第四个位置摆第二种花。

【数据范围】   

  对于 20%数据,有 n ≤8 ,m ≤8 ,ai≤8 ;
  对于 50%数据,有 n≤20,m ≤20,ai≤20;
  对于 100% 数据,有 n≤100,m ≤100,ai≤100 。

【思路梳理】

  dp水题,非常简单的动态规划(只要你,呃,按照我们导师说的,入了门),先设状态函数d[i][j]=用前i种花中的j盆依次摆放所能够形成的不同的方案类型。对于正在填的第i行来说,所需要的每一个数据都来自于上面一行也就是第i-1行,所以可以使用二维/一维的滚动数组来实现。主意,如果使用一维滚动数组的话是要从右往左填的。
  状态转移的过程:正在考虑第i种花时,可以由已经考虑过的第i-1种花转移过来,那么第i种花可以选择limit[i]朵来摆放,也可以选择limit[j]-1朵来摆放,当然,你也可以选择0朵来摆放。所以当我们在考虑d[i][j]时,根据的是d[i-1][j],d[i-1][j-1],etc转移过来的。需要注意到的是,可能不会有d[i-1][j-limit[i]]的状态(Limit[i]可能大于j)所以我们可以得到状态转移方程(大家凑合着看理解吧=_+):

这里写图片描述

  时间复杂度:O(n*m*limit),虽然说有三重循环,但极限情况下仍然非常轻松可以通过(因为数据范围不会很大)。亲测后发现:使用二维滚动数组的速度明显快于使用普通二维数组。

【Cpp代码】
#include<cstdio>
#include<cstring>
#define maxn 105
using namespace std;
int n,m,limit[maxn],d[2][maxn];
//d[i][j]:从前i种花中选择总共j盆来摆放可以形成的方案总数 
//变为二维滚动数组 

int mod(int x)
{
    return x%1000007;
}

void solve()
{
    memset(d,0,sizeof(d));
    d[0][0]=d[1][0]=1;//d[i][0]=1,不摆就是一种方案

    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        int t=0;
        for(int k=0;k<=limit[i] && k<=j;k++)
            t=mod(t+d[(i-1)%2][j-k]);
        d[i%2][j]=mod(t);
    }

    printf("%d\n",mod(d[n%2][m]));
}

int main()
{
//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
    scanf("%d%d",&n,&m);

    for(int i=1;i<=n;i++)   scanf("%d",&limit[i]);
    solve();

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值