acdream 1014: Dice Dice Dice DFS枚举
1014: Dice Dice Dice
Time Limit: 10 Sec Memory Limit: 128 MBDescription
There are 1111 ways in which five 6-sided dice (sides numbered 1 to 6) can be rolled so that the top three numbers sum to 15. Some examples are:
D1,D2,D3,D4,D5 = 4,3,6,3,5
D1,D2,D3,D4,D5 = 4,3,3,5,6
D1,D2,D3,D4,D5 = 3,3,3,6,6
D1,D2,D3,D4,D5 = 6,6,3,3,3
Now we have a extended problem:
In how many ways can n m-sided dice (sides numbered 1 to m) be rolled so that the top k numbers sum to p?
Input
There are multiple test cases. (about 15 groups)
For each test case, there is only four integers n, m, k, p. (1 <= k <= n <= 20, 3 <= m <= 12)
Output
For each test case, output an integer indicating the answer.
Sample Input
5 6 3 15 6 6 3 15
Sample Output
1111 7770
HINT
Source
题目大意: 一个m面的骰子,抛n次,得到序列。统计其中k次最大的和为p的序列数。
解题思路:直接枚举序列,时间总共需要n^m = pow(20,12), 这样时间上不符合。
通过枚举m个值出现的次数,来降低时间复杂度。
因为k,n,m的值都比较小,我们可以在区间[m,1]中枚举出k个满足和为P的序列的个数,且其中最小值为x,然后再在区间[x,1]枚举出n-k个数的数量。
最后得出[1,m]区间各个数出现的次数,则当前合法序列其排列数为 n! / { num1!*num2!*..*numM! } (其中num>0)
View Code
#include<stdio.h> #include<stdlib.h> #include<math.h> #include<string.h> typedef long long LL; int num[30], cnt[30], size[30]; int n, m, K, P; LL fac[21] = {0,1}, ans; void GetFact(){ for(int i = 2; i <= 20; i++) fac[i] = fac[i-1]*i; // for(int i = 1; i <= 20; i++) // printf("%d: %lld\n", i, fac[i]); } void dfs_NK( int x , int CurNum , int MaxNum ) { if( CurNum == MaxNum ) { LL res = fac[n]; for(int i = m; i > 0; i--) { int t = num[i]+cnt[i]; if( t > 0 ) res /= fac[t]; } ans += res; } else{ for(int i = 0; x > 0 && i <= MaxNum-CurNum; i++) { cnt[x] = i; dfs_NK( x-1, CurNum+i, MaxNum ); cnt[x] = 0; } } } void dfs_K( int x, int CurNum, int CurSum) { // printf("CurNum = %d\n", CurNum); if( CurNum == K && CurSum == P ){ /* for(int i = m; i > 0; i--) { printf("num[%d] = %d,", i, num[i]); } printf("\n");*/ memset( cnt, 0, sizeof(cnt)); dfs_NK( x+1, 0, n-K ); return; } for(int i = 0; x > 0 && i <= K-CurNum; i++) { if( CurNum + i <= K && CurSum + i*x <= P ) { num[x] = i; dfs_K( x-1, CurNum+i, CurSum+i*x ); num[x] = 0; } } } int main(){ GetFact(); while( scanf("%d%d%d%d",&n,&m,&K,&P) != EOF) { if( ceil(1.*P/m) > K ) printf("0\n"); else{ ans = 0; memset( num, 0, sizeof(num)); dfs_K( m, 0, 0 ); printf("%lld\n", ans); } } return 0; }