UVA10313 Pay the Price (划分型dp)

题目

题意:
给出 \(N\) , \(l_1\) , \(l_2\) , \(N\) 表示钱的面值,问将N拆分可以有多少种拆分数。
有三种情况:
1,给出 \(N\),将 \(N\) 拆分成面值不超过 \(N\) 的硬币可以有多少种不同的拆分数。
2,给出 \(N\)\(l_1\) ,将 \(N\) 拆分成面值不超过 \(l_1\) 的硬币可以有多少种不同的拆分数。
3,给出 \(N\)\(l_1\)\(l_2\),将 \(N\) 拆分成面值在 \(l_1\)\(l_2\) 范围内的硬币可以有多少种不同的拆分数。

思路:动态规划的思路还是比较明显,但不太好想的就是转移方程,如果用 \(dp[ i ][ j ]\) 表示将面值为 \(i\) 拆分成面值不超过 \(j\) 的硬币可以有多少种拆分。

那么可以用 \(dp[ i - j ][ j ]\) 表示选择面值为 \(j\) 的硬币,\(dp[ i ][ j - 1 ]\) 表示不选择面值为j的硬币。
那么状态转移方程就是: \(dp[ i ][ j ] += dp[ i - j ][ j ] + dp[ i ][ j - 1 ]\)

由于数据给的 \(n<=300\) 那么可以考虑在读入前进行预处理,读入时就可以进行 \(O(1)\) 的查询。

另外,本题的输入比较毒瘤,需要注意

#include<stdio.h>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long ll;
int n;const int MAXN = 305;
ll dp[MAXN][MAXN];

void deal(){
    dp[0][0]=1;
    for(int i=0;i<=300;i++){
        for(int j=1;j<=300;j++){
            if(i-j >= 0)dp[i][j] += dp[i - j][j];
            dp[i][j] += dp[i][j - 1];
        }
    }
}


int main(){
    deal();
    while(~scanf("%d",&n)){
        int a=-1,b=-1;char f = getchar();int ID=0;
        while(f != '\n'){
            if(isdigit(f)){
                if(ID==2){if(b==-1) b=0;b = b*10+f-'0';}
                if(ID==1){if(a==-1) a=0;a = a*10+f-'0';}
            }
            if(f == ' ') ID++;
            f=getchar();
        }
        if(a > 300) a = 300;
        if(b > 300) b = 300;
        if(a != -1 && b == -1){
            if(a > 300) a =300;
            printf("%lld\n",dp[n][a]);
        }
        else if(a == -1 && b == -1){
            printf("%lld\n",dp[n][n]);
        }
        else if(a!=-1 && b!=-1){
            if(a >= 1) printf("%lld\n",dp[n][b] - dp[n][a-1]);
            else printf("%lld\n",dp[n][b]);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/lajioj/p/9301956.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值