ural 1114,计数dp

题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1114

题意:N个盒子,a个红球,b个蓝球,把求放到盒子中去,没有任何限制,有多少种放法。

刚开始我想数学方法啊!想了半天,情况太多了。偷偷搜了一下这个题目,DP,好的,两分钟DP方程出来了。

dp[i][j][k] 表示前i个盒子,用掉了j个红球,k个蓝球,马上dp方程 dp[i][j][k] =∑ dp[i-1][m][n],m=[0,j],n=[0,k];初始化dp[0][0][0] = 1;

要指出的是: 网上有很多解法,m,n的范围是反的,但是也能AC,如果按照这个思路是肯定不对的,至于为啥能AC,我想到了,很多人也许和我的思路刚好相反,他是想把dp[i][j][k] 是还有j个红球没有用,k个蓝球没有用,那么这样的话,循环得反着来,而且初始化应该是dp[0][a][b] = 1;

然而,我这里想提的是,LJH大神的,他就是反着来的,但是超级厉害的地方是,他没有枚举j,k,也就是说没有5层循环,Orz.

用一个数组tsum记录红球k的方案下的答案,sum更新蓝球k的方案下的答案,这样dp[i][j][k] 就可以利用以前的dp[i-1][j][k] 的答案了,只需要不断更新sum,tsum[k],

#include <bits/stdc++.h>
using namespace std; unsigned long long dp[][][];
unsigned long long tsum[]; int main()
{
int n,a,b;
scanf("%d%d%d",&n,&a,&b);
dp[][][] = ; unsigned sum = ; for(int i=;i<=;i++)
{
for(int j=;j<=;j++)
{
for(int k=;k<=;k++)
{
for(int jj = ;jj<=j;jj++)
for(int kk = ;kk<=k;kk++)
dp[i][j][k] += dp[i-][jj][kk];
}
}
} unsigned long long ans = ;
for(int i=; i<=a; i++)
for(int j=; j<=b; j++)
ans +=dp[n][i][j];
printf("%I64u\n",ans); return ;
}
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<deque>
#include<functional>
#include<iterator>
#include<set>
#include<utility>
#include<stack>
#include<queue>
#include<iomanip>
using namespace std; typedef unsigned long long ll; ll dp[][][];
ll tsum[];
int main()
{
int n;
int a,b;
ll sum;
cin>>n>>a>>b;
memset(dp,,sizeof(dp));
dp[][][]=;
for(int i=;i<=n;++i)
{
memset(tsum,,sizeof(tsum));
for(int j=;j<=a;j++)
{
sum=;
for(int k=;k<=b;k++)
{
sum+=dp[i-][j][k];
tsum[k]+=sum;
dp[i][j][k]=tsum[k]; }
}
} sum=;
for(int i=;i<=a;++i)
for(int j=;j<=b;++j)
sum+=dp[n][i][j];
cout<<sum<<endl;
return ;
}
上一篇:js 判断网络状态


下一篇:数组输出字符串