题目链接: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[30][25][25]; unsigned long long tsum[25]; int main() { int n,a,b; scanf("%d%d%d",&n,&a,&b); dp[0][0][0] = 1; unsigned sum = 0; for(int i=1;i<=25;i++) { for(int j=0;j<=20;j++) { for(int k=0;k<=20;k++) { for(int jj = 0;jj<=j;jj++) for(int kk = 0;kk<=k;kk++) dp[i][j][k] += dp[i-1][jj][kk]; } } } unsigned long long ans = 0; for(int i=0; i<=a; i++) for(int j=0; j<=b; j++) ans +=dp[n][i][j]; printf("%I64u ",ans); return 0; }
//#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[21][16][16]; ll tsum[16]; int main() { int n; int a,b; ll sum; cin>>n>>a>>b; memset(dp,0,sizeof(dp)); dp[0][0][0]=1; for(int i=1;i<=n;++i) { memset(tsum,0,sizeof(tsum)); for(int j=0;j<=a;j++) { sum=0; for(int k=0;k<=b;k++) { sum+=dp[i-1][j][k]; tsum[k]+=sum; dp[i][j][k]=tsum[k]; } } } sum=0; for(int i=0;i<=a;++i) for(int j=0;j<=b;++j) sum+=dp[n][i][j]; cout<<sum<<endl; return 0; }