题目解析
题目描述
奇妙的交易国是一个很特殊的国家,在这个国家的货币只有 V0,V1,V2,...,Vn种面值。只要你需要(有这么多钱),你可以让 n 无限大。同时在这个国家有一个很有趣的拍卖场有一条奇怪的规矩:一次交易中,买卖双方只能对每种面值的货币使用不超过两次。
比如,买一件价格 W 为198的物品,V=10 的情况,买家可以使用2张 100 元纸币,卖家则给出物品和2张 1 元纸币。因为奇怪的规则,很多X国人都需要在这个拍卖场交易之前判断一下这个物品是否可以被交易成功。不过很多人并不擅长这种计算,这时候就要请出聪明的你。
题目分析
- 理解货币面值:货币的面值是 V0,V1,V2,...,即 1,V,V2,V3,...。
- 限制条件:每种面值的货币最多只能使用两次。
- 目标:判断是否可以通过这些货币的面值组合来支付物品的价格 W。
解题思路
-
状态定义:
-
我们定义一个三维数组
dp[power][count][W],其中:power表示当前货币面值的指数(即 Vpower)。count表示当前面值货币的使用次数。W表示当前需要支付的金额。
-
dp[power][count][W]表示在当前面值为 Vpower,使用次数为count的情况下,是否可以支付金额W。
-
-
状态转移:
-
如果
W已经为 0,说明可以支付,dp[power][count][W] = true。 -
如果
W小于 0 或者当前面值的货币已经使用超过两次,dp[power][count][W] = false。 -
否则,我们有两个选择:
- 使用当前面值的货币,递归调用
dp[power][count+1][W - V^power]。 - 不使用当前面值的货币,转而使用更高面值的货币,递归调用
dp[power+1][0][W]。
- 使用当前面值的货币,递归调用
-
-
初始条件:
dp[0][0][W]是我们需要求解的状态。
解决代码
#include <iostream>
#include <vector>
#include <cmath>
bool canPay(int V, int W) {
// 动态规划数组,dp[i]表示是否能凑出金额i
std::vector<bool> dp(W + 1, false);
dp[0] = true; // 0元肯定可以凑出
// 使用货币面值V时,最多使用0、1、2张
for (int i = 0; i <= W; ++i) { // 遍历所有已有的金额
if (dp[i]) { // 如果i元钱可以凑出
// 用当前货币面值V加上0张、1张或者2张
if (i + V <= W) dp[i + V] = true;
if (i + 2 * V <= W) dp[i + 2 * V] = true;
}
}
return dp[W]; // 如果dp[W]为true,表示可以支付物品的价格W
}
std::string solution(int V, int W) {
if (canPay(V, W)) {
return "YES";
} else {
return "NO";
}
}
int main() {
// 测试用例
std::cout << (solution(10, 9) == "YES") << std::endl; // 10, 9 => YES
std::cout << (solution(200, 40199) == "YES") << std::endl; // 200, 40199 => YES
std::cout << (solution(108, 50) == "NO") << std::endl; // 108, 50 => NO
return 0;
}