Wannafly挑战赛16 取石子

题目:

给出四堆石子,石子数分别为a,b,c,d。规定每次只能从堆顶取走石子,问取走所有石子的方案数。

样例:

3 5 4 2

输出:

2522520

数学垃圾硬是乱dp:

/**
这就是数学垃圾的下场!!!!!
dp[i][j]表示只有两堆石子时有多少种取法。
f[i][j]表示把长度为j的序列 插入到长度为i的序列中 而且序列 i,j都保持原有得次序 的方法有多少种。
则 ans = f[a+b][c+d]*dp[a][b]*dp[c][d];
*/
#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int maxn = 500;
const int inf = 1e9;
const ll mod = (1e9+7);
ll dp[maxn+5][maxn+5],f[maxn*2+5][maxn*2+5];
ll dfs(int a,int b)
{
    if(dp[a][b]!=-1) return dp[a][b];
    if(a==0&&b==0) return (dp[a][b] = 1);
    dp[a][b] = 0;
    if(a>0) dp[a][b] = (dp[a][b]+ dfs(a-1,b))%mod;
    if(b>0) dp[a][b] = (dp[a][b]+ dfs(a,b-1))%mod;
    return dp[a][b]%mod;
}
ll DFS(int a,int b)
{
    if(f[a][b]!=-1) return f[a][b];
    if(a==0||b==0) return (f[a][b] = 1);
    f[a][b] = 0;
    if(b>0) f[a][b] = (f[a][b] + DFS(a,b-1))%mod;
    if(a>0) f[a][b] = (f[a][b] + DFS(a-1,b))%mod;
    return f[a][b];
}
int main()
{
    memset(dp,-1,sizeof(dp));
    memset(f,-1,sizeof(f));
    int a,b,c,d;
    while(~scanf("%d %d %d %d",&a,&b,&c,&d))
    {
        int sum0 = a+b,sum1=c+d;
        ll w1= dfs(a,b), w2 = dfs(c,d);
        ll w = DFS(sum0,sum1);
        ll ans = (w*w1%mod*w2%mod)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

被自己菜醒的组合数学:

可以到着思维:把石头取出 等价 把石头放回,ans = C(a, a+b+c+d)*C(b, b+c+d)*C(c, c+d) = (a+b+c+d)!/(a!b!c!d!).

也可以顺着来,把石头取出当是每堆的石头顺序是固定的:大神分析的很好:点击打开链接


猜你喜欢

转载自blog.csdn.net/qq_32944513/article/details/80516003
今日推荐