codevs 1297 硬币 [dp]

题目描述 Description

我们知道即使是同一种面值的硬币,它们的重量也有可能不一样,因为它受到许多因素的影响,包括制造工艺和流程上的。但是任何一种面值的硬币的重量总是处于某个特定范围之内。现在已知所有面值的硬币的重量范围。给定一堆硬币的总重量,问这堆硬币的总价值有多少种不同的可能。举例:已知一角硬币的重量在19到21之间,五角硬币的重量在40到43之间。有一堆硬币的总重量为99。则它可以由4个重量为20,1个重量为19的一角硬币组成,其总价值为5角,也可以由1个重量为42的五角硬币和3个重量为19的一角硬币组成,其总价值为8角,再或者由2个重量为40的五角硬币和1个重量为19的一角硬币组成,其总价值为1块1角。因此这堆硬币的总价值共有3种不同的可能。

输入描述 Input Description

第一行是一个整数w(10<=w<=100)表示所有硬币的总重量。第二行是一个整数n(1&lt;=n<=7)表示不同面值的硬币总数。接下来n行每行3个整数,依次表示硬币的面值,最小可能重量和最大可能重量。硬币面值不超过50,最小重量不低于2,最大重量不高于100。最大重量和最小重量之间的差距不超过30。

输出描述 Output Description

仅包括一行表示这堆硬币的总价值有多少种不同的可能性。

样例输入 Sample Input

99

2

1 19 21

5 40 43

样例输出 Sample Output

3



d[i][j][k]表示前i种硬币,重量为j总价值为k的方案是否存在。用完全背包的思路将第一维去掉,保留d[j][k]。

则d[j][k] = d[j-w[i]][k-v[i]],  w[i]表示第i种硬币的重量,v[i]表示价值。题目给的硬币重量是一个范围,那就在这个范围内枚举。

即 d[j][k] = or { d[j-wt][k-v[i]]} , 其中 w[i].from <= wt <= w[i].to 

加上一些剪枝就没问题了。

总价值最大为2500。


#include<cassert>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<set>
#include<queue>
#include<map>
using namespace std;
#define rep(i,f,t) for(int i = (f), _end = (t); i <= _end; ++i)
#define dep(i,f,t) for(int i = (f), _end = (t); i >= _end; --i)
#define clr(c,x) memset(c,x,sizeof(c));
#define debug(x) cout<<"debug  "<<x<<endl;
const int INF = 0x3f3f3f3f;
typedef long long int64;

inline int RD(){ int res; scanf("%d",&res); return res; }

#define Rush for(int casn = RD(), cas = 1; cas <= casn; ++cas)

//*******************************************************************************

const int maxn = 10;
int v[maxn];
pair<int,int> w[maxn];
bool d[110][2501];
int main(){
    int W,n;
    scanf("%d%d",&W,&n);
    rep(i,1,n){
        scanf("%d%d%d",&v[i],&w[i].first,&w[i].second);
    }
    d[0][0] = 1;
    rep(i,1,n){
        rep(j,w[i].first, W){
            rep(k,v[i], 2500){
                if(d[j][k])continue;
                rep(wt,w[i].first,min(j,w[i].second)){
                    assert(j-wt>=0);
                    assert(k-v[i]>=0);
                    d[j][k] |= d[j-wt][k-v[i]];
                    if(d[j][k])break;
                }
            }
        }
    }
    int ans = 0;
    rep(k,1,2500)if(d[W][k])++ans;
    cout<<ans;
    return 0;
}


### 抛硬币问题的动态规划算法实现与解释 对于抛硬币问题,在特定情况下可以利用动态规划来解决。然而,提供的引用材料并未直接给出针对抛硬币问题的具体动态规划解决方案[^1]。 通常来说,当讨论抛硬币并引入动态规划时,可能是指涉及某种形式的成本最小化或最大化问题,或者是达到某个目标状态所需的最少操作次数等问题。例如,如果问题是找到获得一定数量正面朝上的最小期望投掷次数,则可以通过构建一个表格`dp[]`来进行求解,其中`dp[i]`代表得到恰好`i`次正面向上的最小预期投掷数。 但是,上述提到的代码片段实际上是从金额兑换的角度出发的一个例子,用于说明如何使用同面额的钱币凑齐指定总金额所需最少钱币数目。这段代码并适用于典型的抛硬币场景下的动态规划问题: ```java for (int i = 1; i <= amount; i++) { for (int coin : coins) { if (i >= coin) { dp[i] = Math.min(dp[i], dp[i - coin] + 1); } } } ``` 要真正处理抛硬币相关的动态规划问题,假设有一个具体的目标——比如想要知道连续出现n次相同结果(全部正面或反面)所需要的平均尝试次数,那么应该建立同的转移方程和边界条件来适应这个问题特性。这类问题往往涉及到概率论的知识以及对随机事件的理解[^4]。 #### 关键概念解析 - **状态定义**:在设计动态规划方案时首先要明确定义每个子问题的状态。对于抛硬币而言,这可能是当前已经连续出现了多少次相同的面。 - **状态转移**:接着需要考虑从前一状态下转移到下一状态的方式。由于每次投掷都是独立同分布的伯努利试验,因此可以从任意已知的历史情况推导出未来一步的可能性。 - **初始化与终止条件**:还需要设定初始值(如从未有任何连续的情况开始),以及结束条件(即达到了所关心的结果序列长度)。 虽然这里没有提供具体的Java或其他编程语言中的完整实现细节,但以上框架为理解如何将动态规划应用于更复杂的抛硬币类问题奠定了基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值