您的位置:首页 > 其它

Skyscrapers 组合数学

2014-04-03 21:03 274 查看




题目大意:对于一行n个数的数组,从左边(必须包含第一个)的最长递增长度为left,从右边为right,求符合条件的情况数。

例1:1 3 2和  2 3 1

例2:4 1 2 3 和 4 2 1 3

1.由于最高的那个一定能被看到,所以可以枚举highest的位置。

设dp[i][j]为i个数有j个递增的种数

2.对于位置i,从n-1个数里选i-1个数放左边,并且左边有left个递增,C(n-1,i-1)*dp[i-1][left-1];

剩下的数在右边,并且有right个递增, 再*dp[n-i][right-1]。

答案就是 sum(C(n-1,i-1)*dp[i-1][left-1]*dp[n-i][right-1]) ;

由于左右个数要足够,所以 i=[left,n+1-right];

那么问题就是如何求dp[i][j]了。

3.对于dp[i][j],分两种情况。dp[i-1][j-1] 那么最大的那个放最右。dp[i-1][j] 那么最大的那个放最右,然后i-1个选一个放最大的后面。

有状态转移为dp[i][j]=dp[i-1][j-1]+dp[i-1][j]*(i-1)

4.根据C(i,j)=C(i-1,j-1)+C(i-1,j)递推求出c[i][j]。再注意MOD,即可求出答案。

题目地址

源码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long  LL;
const int MOD=1e9+7,N=5001;

LL dp

,c

;

void init()  {
//c[i][j]表示i个中取j个的种数
for(int i=0;i<N;i++)
c[i][i]=c[i][0]=1;
for(int i=2;i<N;i++){
for(int j=1;j<i;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
}
//dp[i][j]表示 i个里j个递增的种数
dp[0][0]=1;
for(int i=1;i<N;i++){
for(int j=1;j<=i;j++)
dp[i][j]=((i-1)*dp[i-1][j]%MOD+dp[i-1][j-1])%MOD;
}
}

void solve(int n,int left,int right){
LL ans=0;
//枚举最高的位置--最高的肯定能被看到
for(int i=left;i<=n-right+1;i++){
ans=(ans+((c[n-1][i-1]*dp[i-1][left-1])%MOD)*dp[n-i][right-1]%MOD)%MOD;
}
printf("%I64d\n",ans);
}

int main(){
init();
int n,left,right;
while(cin>>n>>left>>right&&n||left||right)
solve(n,left,right);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: