POJ3181--Dollar Dayz

来源:互联网 发布:淘宝9.9元天天特价包邮 编辑:程序博客网 时间:2024/05/17 04:11

题目大意:给出两个整数n,m,求m以内的整数组成n有多少种情况

 

分析:这题数据超大,使用了将大数分开处理的方法。参考了别人的分析http://blog.csdn.net/libin56842/article/details/9455979。接下来,先介绍一下整数划分的问题。

 

所谓整数划分,是把一个整数N拆分成一组数相加并且等于N的问题。

例如:

6

5+1

4+2,4+1+1

3+3,3+2+1,3+1+1+1

2+2+2,2+2+1+1,2+1+1+1+1

1+1+1+1+1+1

 

假设F(N, M)表示划分个数,M表示将N拆分后的序列中最大不超过M

 

考虑边界状态:

N=1或者M=1都只有一种方法,即F(1, 1) = 1

M=N时,等于最大数为M-1时加上1(就是N本身),F(N, N) = F(N, N-1)+1

M>N时,最大数比N大就没有意义了,所以,F(N, M) = F(N, N)

M<N时,等于序列中最大数为M-1的划分和N-M的划分之和,例如,F(6, 4)等于对3的划分,加上2的划分。F(N, M) = F(N, M-1) + F(N-M, M)

 

状态:dp[i][j]表示整数i最大数不超过j的划分个数

状态转移方程为:dp[i][j] = dp[i][j-1]+dp[i-j][j]


代码:

#include <cstdio>#include <cstring>using namespace std;long long dp[1111][111][2];#define INF 100000000000000000int main() {    int n, k;    scanf("%d%d", &n, &k);    memset(dp, 0, sizeof(dp));    for(int i = 0; i <= k; i++)        dp[0][i][1] = 1;    for(int j = 1; j <= k; j++) {        for(int i = 1; i <= n; i++) {            if(i < j) {                dp[i][j][0] = dp[i][j-1][0];                dp[i][j][1] = dp[i][j-1][1];            }            else {                dp[i][j][0] = dp[i][j-1][0]+dp[i-j][j][0];                dp[i][j][1] = dp[i][j-1][1]+dp[i-j][j][1];                dp[i][j][0] += dp[i][j][1]/INF;     //大数的高位                dp[i][j][1] = dp[i][j][1]%INF;       //大数的低位            }        }    }    if(dp[n][k][0]) printf("%lld", dp[n][k][0]);    printf("%lld\n", dp[n][k][1]);    return 0;}



0 0
原创粉丝点击