【DP】TOJ 2820 How many different ways

【DP】TOJ 2820 How many different ways

给定从1 到 N的 N 个数,问有多少种不同方案划分这些数。比如N = 3,则有5种方案:
{{1},{2},{3}}
{{1,2},{3}}
{{1,3},{2}}
{{2,3},{1}}
{{1,2,3}}
最后的结果只保留后四位,即mod10000;
上网查了下,集合的划分的个数叫做bell数,bell数可以递归求解:
bell[0] = 1;
bell [n + 1] = sigma(C(n,k))*(bell[k]); (0<=k<=n)

 
然而这个题却不可以这样做,因为N得范围是2000,这样做必定超时,于是想到了DP,如果用dp[i][j]表示i个数
划分成j个集合,那么便有dp[i][j] = j * dp[i-1][j] + dp[i-1][j-1];( i > j )直观理解就是,将i个数划分成j个集合的个数,即为i-1个数划分到j个集合的数,再将多的那个依次放到j个集合中,所以乘以j,或者是i-1个数放在j-1个集合中,第j个集合为空,则正好将多的这个数放到这个集合中,于是便有上边的状态转移方程。
Code:

 

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

int map[2002][2002];
void DP(){
    memset(map,
0,sizeof(map));
    
int i,j,k;
    
for(i = 0;i <= 2000; i++){
        map[i][i] 
= 1;
        map[i][
1= 1;
    }

    
for(i = 0;i <= 2000 ;i++)
        
for(j = 0; j < i; j++)
            map[i][j] 
= (j * map[i-1][j] + map[i-1][j-1])%10000;
}

int main()
{
    
int i,j,k,n;
    DP();
    
while(scanf("%d",&n),n){
        
int ans = 0;
        
for(i = 0;i <= n; i++)
            ans 
= (ans + map[n][i])%10000;
        
string str = "0000";
        str[
3= ans%10+'0'; ans/=10;
        str[
2= ans%10+'0'; ans/=10;
        str[
1= ans%10+'0'; ans/=10;
        str[
0= ans%10+'0'; ans/=10;
        cout
<<str<<endl;
    }

}

你可能感兴趣的:(【DP】TOJ 2820 How many different ways)