题目:点击打开链接
做法:“对于所有颜色为i (1<=i<=k-1)的球,他的最后一个球总是在编号比他大的球拿完之前拿完”,也就意味着,i拿完的时候,不可以有它前面的球没拿完。
假设现在拿完了第i种球,准备在其中插入第i+1种球(第i+1种球有t[i+1]个),就相当于,在这些球的最后一定要放一个i+1号球,设没放i+1号球的球数为cnt,cnt个球会产生cnt+1个空,问题就转换成了:在cnt+1个空中,放入t[i+1]-1个球的放法有多少种。然后递推就可以了。
“在cnt+1个空中,放入t[i+1]-1个球的放法有多少种”这个问题,可以使用dp来完成,dp[i][j]表示 前i个空放了前j个球有多少种放法,dp方程就是: dp[i][j] = sigma dp[i-1][k] k<=j 朴素是需要O(n^3)的,我们可以用一个sum数组,sum[x]表示 sigma dp[i-1][k] k<=x,这样就能O(n^2)完成了。
DP数组处理出来之后,就一步步递推就行了,注意mod
#include<iostream>
#include<string.h>
#include<string>
#include<algorithm>
#include<stdio.h>
#include<vector>
#define LL long long int
using namespace std;
LL mod = 1000000007;
const int maxn = 1200;
LL dp[maxn][maxn];
LL sum[maxn];
void process()
{
memset(sum,0,sizeof(sum));
for(int i = 0;i<maxn;i++)
{
dp[i][0] = 1;
sum[i] = 1;
}
for(int i = 1;i<maxn;i++)
{
LL s = 0;
for(int j = 1;j<maxn;j++)
{
dp[i][j] = sum[j];
sum[j] = sum[j-1] + dp[i][j];
sum[j] %= mod;
}
}
}
LL arr[maxn];
int k;
int main()
{
process();
while(cin>>k)
{
for(int i = 1;i<=k;i++)
{
scanf("%lld",&arr[i]);
}
LL ans = 1;
LL cnt = arr[1];
for(int i = 2;i<=k;i++)
{
if(arr[i] == 1) {}
else
{
ans *= dp[cnt+1][arr[i]-1];
ans %= mod;
}
cnt += arr[i];
}
cout<<ans<<endl;
}
return 0;
}