HDU 4248 A Famous Stone Collector (dp)

来源:互联网 发布:软件开发是什么? 编辑:程序博客网 时间:2024/06/05 23:07

题意:

给你一些不同颜色的石头,问选出一些石头排成一排总共有多少种不同排法,不同数量的石头视为不同情况,每个位置上的石头颜色都相同视为相同情况。


解题思路:

首先对于k个石头,有x种不同颜色石头,每种颜色石头分别有ki个,那么这k个石头排成一排总共有k!/( k1!*k2!*k3!..kx!)种情况数,所以可以设dp[i][j]表示选完前i种颜色石头总共选了j个石头的情况数

状态转移方程就是 dp[i][j+k] = dp[i-1][j] / k! ,这里 / k!就是乘以k!的逆元。


#include <stdio.h>#include <string.h>typedef __int64 ll;const int mod = 1000000007;const int maxn = 10000 + 10;ll exgcd(ll a, ll b, ll &x, ll &y) {    if(!b){        x = 1; y = 0;        return a;    }    ll ret = exgcd(b, a%b, y, x);    y -= a/b*x;    return ret;}ll inv(ll n, ll mod) {    ll x, y, d = exgcd(n, mod, x, y);    return (x%mod + mod)%mod;}ll fac[maxn], ff[maxn];void init(int n) {    fac[0] = 1;    ff[0] = inv(1, mod);    for(int i = 1;i <= n; i++) {        fac[i] = fac[i-1]*i%mod;        ff[i] = inv(fac[i], mod);    }}ll dp[105][10005];int a[111];int main() {    init(10000);    int n, cas = 1;    while(scanf("%d", &n) != -1) {        for(int i = 1;i <= n; i++)            scanf("%d", &a[i]);        int tot = 0;        memset(dp, 0, sizeof(dp));        dp[0][0] = 1;        for(int i = 1;i <= n; i++) {            for(int j = 0;j <= tot; j++) {                for(int k = 0;k <= a[i]; k++) {                    dp[i][j+k] += dp[i-1][j]*ff[k];                    if(dp[i][j+k] >= mod)   dp[i][j+k] %= mod;                }            }            tot += a[i];        }        ll ans = 0;        for(int i = 1;i <= tot; i++)            ans += dp[n][i]*fac[i]%mod;        printf("Case %d: %I64d\n", cas++, ans%mod);    }    return 0;}


原创粉丝点击