当前位置:编程学习 > C/C++ >>

HDU4372 Count the Buildings

2012 Multi-University Training Contest 8
1003题

N座高楼,高度均不同且为1~N中的数,从前向后看能看到F个,从后向前看能看到B个,问有多少种可能的排列数。
0 < N, F, B <= 2000

组合数学。
ans(n, f, b) = C[f + b - 2][f - 1] * S[n - 1][f + b - 2];
C:组合数
    因为最高的楼层必然是N,我们只考虑两边能看到的楼层的数目f + b - 2。
    第二项f - 1和b - 1是等效的,代表一侧能看到的楼层的数目减去最高的那个楼层,这样所有能看到的楼层就被分配完了。
    C[n][m] = C[n-1][m-1] + C[n-1][m]
S:第一类Stirling数
    上面组合完成后,对于每一组,求它去掉最高楼层后的第一类Stirling数,每个圈选择最高的作为可以看到的楼层。
    S[n][m] = S[n-1][m-1] + S[n-1][m] * (n - 1)
两者相乘即为答案。

[cpp] 
#include <cstdio> 
#include <cstring> 
#include <algorithm> 
using namespace std; 
const int MAXN = 2010; 
const int MOD = 1000000007; 
 
int c[MAXN][MAXN]; 
int s[MAXN][MAXN]; 
bool vc[MAXN][MAXN]; 
bool vs[MAXN][MAXN]; 
 
int C(int n, int m) 

    if(vc[n][m]) 
    { 
        return c[n][m]; 
    } 
    if(m > (n >> 1)) 
    { 
        return C(n, n - m); 
    } 
    vc[n][m] = true; 
    if(m == 0) 
    { 
        return c[n][m] = 1; 
    } 
    if(m == 1) 
    { 
        return c[n][m] = n; 
    } 
    return c[n][m] = (C(n - 1, m - 1) + C(n - 1, m)) % MOD; 

 
int S(int n, int m) 

    if(vs[n][m]) 
    { 
        return s[n][m]; 
    } 
    if(n < m || m == 0) 
    { 
        return 0; 
    } 
    vs[n][m] = true; 
    if(n == 1 && m == 1) 
    { 
        return s[n][m] = 1; 
    } 
    return s[n][m] = ( S(n-1, m-1) + ( (__int64)S(n-1, m) * (n - 1) ) % MOD ) % MOD; 

 
int solve(int n, int f, int b) 

    if(f + b - 1 > n) 
    { 
        return 0; 
    } 
    return ( (__int64)C(f + b - 2, f - 1) * S(n - 1, f + b - 2) ) % MOD; 

 
int main() 

    int n, f, b; 
    int caseNumber; 
    scanf("%d", &caseNumber); 
    while(caseNumber--) 
    { 
        scanf("%d%d%d", &n, &f, &b); 
        printf("%d\n", solve(n, f, b)); 
    } 
    return 0; 


补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,