poj 3181 Dollar Dayz

整数划分问题,用动态规划来做

整数划分是把一个正整数 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) 整数 N 的划分个数,其中 M 表示将 N 拆分后的序列中最大数

考虑边界状态:
M = 1 或者 N = 1 只有一个划分 既: F(1,1) = 1
M = N : 等于把M - 1 的划分数加 1 既: F(N,N) = F(N,N-1) + 1 
M > N: 按理说,N 划分后的序列中最大数是不会超过 N 的,所以 F(N,M ) = F(N,N)
M < N: 这个是最常见的, 他应该是序列中最大数为 M-1 的划分和 N-M 的划分之和, 比如F(6,4),上面例子第三行, 他应该等于对整数 3 的划分, 然后加上 2 的划分(6-4) 所以 F(N,M) = F(N, M-1) + F(N-M,M)


用动态规划来表示


 dp[n][m]= dp[n][m-1]+ dp[n-m][m]
           
           dp[n][m]表示整数 n 的划分中,每个数不大于 m 的划分数。
           则划分数可以分为两种情况:
 
           a. 划分中每个数都小于 m, 相当于每个数不大于 m- 1, 故
              划分数为 dp[n][m-1].
 
           b. 划分中有一个数为 m. 那就在 n中减去 m , 剩下的就相当
              于把 n-m 进行划分, 故划分数为 dp[n-m][m];


由于数量巨大,用64位也表示不了,在这里的处理方式分为两截来处理,具体详见代码


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

long long a[1100][110],b[1100][110],inf;

int main(){
    int n,k,i,j;
    for(inf=1,i=0;i<18;i++) inf*=10;
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    scanf("%d%d",&n,&k);
    for(i=0;i<=k;i++) a[0][i]=1;  
    for(i=1;i<=k;i++){
        for(j=1;j<=n;j++){
            if(j-i<0){
                b[j][i]=b[j][i-1];   
                a[j][i]=a[j][i-1];
                continue;
            }
            b[j][i]=b[j][i-1]+b[j-i][i]+(a[j][i-1]+a[j-i][i])/inf; //前半截的处理
            a[j][i]=(a[j][i-1]+a[j-i][i])%inf; //后半截
        }
    }
    if(b[n][k]) printf("%I64d",b[n][k]);  //两截一起输出
    printf("%I64d\n",a[n][k]);
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值