Vijos 1060 盒子(DP)

【问题描述】

  n 个盒子排成一行(编号为1..n)。你有A个红球和B个蓝球。球除了颜色没有任何区别。你可以将球放进盒子。一个盒子可以同时放进两种球,也可以只放一种,也可以空着。球不必全部放入盒子中。编程计算有多少种放置球的方法。

【输入格式】

  一行,n,A,B,用空格分开。

【输出格式】

  一行,输出放置方案总数。

【输入样例】

2 1 1

【输出样例】

9

【样例解释】

  用一对括号表示一个盒子,R表示红色,B表示蓝色,有如下9种方案:
   ( ), ( )
   (R ), ( )
   (B ), ( )
   (RB), ( )
   (R ), (B )
   (B ), (R )
   ( ), (R )
   ( ), (B )
   ( ), (RB)

【数据范围】

1<=n<=20 , 0<=A<=15, 0<=B<=15

这道题计算多少种放置球的方法,一个盒子里可以放多个球。可以设状态函数f(i,j,k)代表前i个箱子里放j个红球,k个蓝球的方案数。此时的方程f(i,j,k)= Σ f(i-1,j-x,k-y)(0<=x<=j&&0<=y<=k),边界为f(0,0,0)=1(前0个箱子里放0个红球,0个蓝球的方案数为1),用五重循环实现,最后的答案就是f(n,a,b)= Σ f(i-1,x,y)(0<=x<=j&&0<=y<=k)。时间复杂度为a*a*b*b*n.因为答案最大达到2^64-1,所以记得用unsigned long long。
如果要优化,可以设状态函数f(i,j)代表前i个箱子里放j个蓝球或红球的方案数,把蓝球和红球单独放,方程为f(i,j)= Σ f(i-1,j-k)(0<=k<=j),边界是f(i,0)=1,f(0,i)=1。最后答案为f(n,a)*f(n,b)。此时时间复杂度为(max(a,b)^2*n/2)。
如果只用两重循环,可以把t初始化放在循环外,每次循环j时t累加一边f(i-1,j),最后f(i,j)转存t。时间复杂度为(max(a,b)*n)。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a,b,n;
unsigned long long d[22][22];

//f(i,j)前i个箱子装只放红球或蓝球不超过j球的方案数
//f(i,j)=f(i-1,0)+f(i-1,k) 0<=k<=a||k<=b;
int main()
{
    //freopen("box.in","r",stdin);
    scanf("%d %d %d",&n,&a,&b);


    memset(d,0,sizeof(d));
    for(int i=0;i<=n;i++)
    {
        d[i][0]=1;
    }
    for(int i=1;i<=a||i<=b;i++)
    {
        d[0][i]=1;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=a||j<=b;j++)
        {
            unsigned long long t=0;
            for(int k=0;k<=j;k++)
            {
                t+=d[i-1][k];
            }
            d[i][j]=t;
        }

    }
    printf("%I64u",(unsigned long long)d[n][a]*d[n][b]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值