HDU_4248_A Famous Stone Collector(数学+DPの組み合わせ)

1748 ワード

題型:数論
タイトル:
N山の石があって、それぞれの山の色は同じで、任意の2山の色は違います.
すべての石が何種類の異なる配列に並ぶことができるかを聞く.
分析:
挿空法を採用する思想:
dp(i,j)は,前i堆石で長さjのシーケンスに並べられたことを示す.
dp(i,j)という状態については、2つの状態から押し出される.
(1)第iの石を置かず、直接前i-1の石から長さjのシーケンス、すなわちdp(i-1,j)を構成する.
(2)第iの石の中からx個を取り出してシーケンスに入れる長さjのシーケンス,すなわちdp(i−1,j−x)*C(j,x)を構成する.C(j,x)は、j個の位置からx個の位置を選択する組合せ数である.
だから状態遷移方程式:
                                       dp(i,j) = dp(i-1,j) + ∑(dp(i-1,j-x)*C(j,x))
コード:
#include
#include
#include
#include
#define LL long long
#define mt(a,b) memset(a,b,sizeof(a))
using namespace std;

const LL mod = 1e9+7;
const int MAXN = 12345;

int n;
LL dp[123][MAXN];
LL c[MAXN][123];
int a[123];

void cal() {
	int x = 10000;
	int y = 100;
	mt(c,0);
	int i,j;
	for(i=0; i<=y; i++)c[0][i]=c[1][i]=1;
	for(i=0; i<=y; i++)c[i][i]=1;
	for(i=0; i<=x; i++)c[i][0]=1;
	for(i=1; i<=x; i++)
		for(j=1; j<=y; j++)
			if(i!=j)
				c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}

void DP(){
	mt(dp,0);
	for(int i=1;i<=n;i++){
		dp[i][0] = 1;
	}
	for(int i=1;i<=a[1];i++){
		dp[1][i] = 1;
	}
	int sum = a[1];
	for(int i=2;i<=n;i++){
		sum += a[i];
		for(int j=1;j<=sum;j++){
			int minn = min(j,a[i]);
			dp[i][j] = dp[i-1][j];
			for(int k=1;k<=minn;k++){
				dp[i][j] += dp[i-1][j-k]*c[j][k];
				dp[i][j] %= mod;
			}
		}
	}
}

int main(){
	int _ = 0;

	cal();
	while(~scanf("%d",&n)){
		int sum = 0;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			sum += a[i];
		}

		DP();

		LL ans = 0;
		for(int i=1;i<=sum;i++){
			ans += dp[n][i];
			ans %= mod;
		}

		printf("Case %d: %lld
",++_,ans); } return 0; }