编程刷题-动态规划类型

KY7 整数拆分

描述

一个整数总可以拆分为2的幂的和,例如: 7=1+2+4 7=1+2+2+2 7=1+1+1+4 7=1+1+1+2+2 7=1+1+1+1+1+2 7=1+1+1+1+1+1+1 总共有六种不同的拆分方式。 再比如:4可以拆分成:4 = 4,4 = 1 + 1 + 1 + 1,4 = 2 + 2,4=1+1+2。 用f(n)表示n的不同拆分的种数,例如f(7)=6. 要求编写程序,读入n(不超过1000000),输出f(n)%1000000000。

输入描述:

每组输入包括一个整数:N(1<=N<=1000000)。

输出描述:

对于每组数据,输出f(n)%1000000000。

示例1

输入:

7

输出:

6

代码

思路:
每一个数都可以拆成2的次幂的形式,2的次幂有(1,2,4,6,8...),其中只包括一个奇数,那就是1。

假设给定一个数N:
1.N是奇数,那么N拆分的时候必定有一个1,因为2的次幂除了1都是偶数,由很多偶数不可能构成奇数。
所以 f(N)=f(N-1)
举例:
5=(1+4)=(1+2+2)=(1+1+1+2)=(1+1+1+1+1) ;一共有四种拆分形式
4=(4) =(2+2) =(1+1+2) = (1+1+1+1) ;一共四种拆分方式


2.N是偶数,那么可以分成两种情况:
2.1 N中拆分出1,N中拆分出1和上边N是奇数的情况一样,即f(N-1)
举例:
4=(4)=(2+2)=(1+1+2)=(1+1+1+1)
3=(1+2)=(1+1+1)
2.2 N中不拆分出1
不拆分出1,即都是偶数,偶数的性质可知,偶数都可以整除2,假如N的和为2m,那么除2之后变成m,但是并不影响f(N)。
举例:
4=(4)=(2+2) 都不包含1

同理,由2.1和2.2两种情况的结果相加得到完整的4的拆分。
也就是说N为偶数的情况就是f(N)=f(N-1)+f(N/2)---------------好看一点儿就是:f(2m)=f(2m-1)+f(m);

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
long long dp[1000002];
int n;
int main() {
    dp[0]=1;
    while(cin>>n){
        for(int x=1;x<=n;x++){
            if(x%2==0){  //如果为偶数,那么分成两种情况,一种有组成部分1,一种全为偶数组成部分
                dp[x]=(dp[x-1]+dp[x/2])%1000000000;
            }
            else{ //如果为奇数,那么一定含有1
                dp[x]=dp[x-1]%1000000000;
            }
            
        }
        cout<<dp[n]%1000000000<<endl;
    }
    return 0;
}

KY58 神奇的口袋    01背包

描述

有一个神奇的口袋,总的容积是40,用这个口袋可以变出一些物品,这些物品的总体积必须是40。John现在有n个想要得到的物品,每个物品的体积分别是a1,a2……an。John可以从这些物品中选择一些,如果选出的物体的总体积是40,那么利用这个神奇的口袋,John就可以得到这些物品。现在的问题是,John有多少种不同的选择物品的方式。

输入描述:

输入的第一行是正整数n (1 <= n <= 20),表示不同的物品的数目。接下来的n行,每行有一个1到40之间的正整数,分别给出a1,a2……an的值。

输出描述:

输出不同的选择物品的方式的数目。

示例1

输入:

3
20
20
20

输出:

3

代码

【01背包问题】从后往前滚动数组

【要求凑整数】边界值:[0][0]=1,0枚硬币凑0面值、算一种方案;[0][j]=0

【统计方案数】转移方程:a+b

#include <bits/stdc++.h>
using namespace std;
int n;
int a[21];
int dp[41];
int main(){
    dp[0]=1;   //当为0的时候,不选就是一种方案
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        for(int j=40;j>=a[i];j--){     //必须要从大到小,因为有时间因素存在,这里第二个循环内的dp表示的都是只考虑前i个物品。
            dp[j]=dp[j]+dp[j-a[i]];
        }

    }
    cout<<dp[40]<<endl;
    return 0;
}

KY13 最小邮票数

描述

    有若干张邮票,要求从中选取最少的邮票张数凑成一个给定的总值。     如,有1分,3分,3分,3分,4分五张邮票,要求凑成10分,则使用3张邮票:3分、3分、4分即可。

输入描述:

    有多组数据,对于每组数据,首先是要求凑成的邮票总值M,M<100。然后是一个数N,N〈20,表示有N张邮票。接下来是N个正整数,分别表示这N张邮票的面值,且以升序排列。

输出描述:

      对于每组数据,能够凑成总值M的最少邮票张数。若无解,输出0。

示例1

输入:

10
5
1 3 3 3 4

输出:

3


代码

这其实是一个背包问题,背包容量为 m,放邮票使得邮票数最少。定义一个二维数组dpdp[i][j]表示用前i张邮票组成总值为j所需的最小邮票数。对于第i张邮票,其实我们只有两种选择啦,选或者是不选。那么选或者不选如何决定呢,这就要取决于怎样才能使得dp[i][j]的值最小啦。所以状态转移方程就是dp[i][j]=min(dp[i-1][j],dp[i-1][j-stamps[i]]+1)

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int n,m;
const long long INF=100000;
int main() {
    while(cin>>m>>n){
        vector<vector<int>> dp(n+1,vector<int>(m+1,INF));  //前i个邮票组成邮票总值j时的的邮票张数
        vector<int> stamp(n+1,0);
        for(int i=1;i<=n;i++){
            cin>>stamp[i];
        }
        dp[0][0]=0;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=m;j++){
                if(j<stamp[i]){
                    dp[i][j]=dp[i-1][j];
                    continue;
                }  
                else {
                    dp[i][j]=min(dp[i-1][j],dp[i-1][j-stamp[i]]+1);
                }
            }
        }
        if(dp[n][m]==INF){
            cout<<0<<endl;
        }
        else cout<<dp[n][m]<<endl;

    }
    return 0;
}
// 64 位输出请用 printf("%lld")

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值