42点(枚举/动态规划)
0.题目介绍
【题目描述】
请你设计一个程序对该问题进行解答。
众所周知在扑克牌中,有一个老掉牙的游戏叫做 24 点,选取 4 张牌进行加减乘除,看是否能得出 24 这个答案。
现在小蓝同学发明了一个新游戏,他从扑克牌中依次抽出6张牌,注意不是一次抽出,进行计算,看是否能够组成 42 点,满足输出 YES,反之输出 NO。
最先抽出来的牌作为第一个操作数,抽出牌做第二个操作数,运算结果再当作第一个操作数,继续进行操作。
注:除不尽的情况保留整数,而且扑克牌的四张 10都丢了,不会出现 10。
请设计一个程序对该问题进行解答。
【输入描述】
输出仅一行包含 6 个字符。
保证字符 ∈ 3 4 5 6 7 8 9 10 J Q K A 2。
【输出描述】
若给出到字符能够组成 42点 , 满足输出 YES,反之输出 NO。
1.题解
1.1 DFS暴力回溯
思路
由于一共就六张牌,可以使用DFS直接暴力回溯
代码
#include<bits/stdc++.h>
using namespace std;
bool flag = false;
int numList[6];
int getNum(char ch) {
if(ch == 'A') return 1;
else if (ch == 'K') return 13;
else if (ch == 'Q') return 12;
else if (ch == 'J') return 11;
else return ch - '0';
}
void DFS(int x, int sum) {
if(x == 6) {
if(sum == 42) {
flag = true;
}
return;
}
DFS(x + 1, sum + numList[x]);
DFS(x + 1, sum - numList[x]);
DFS(x + 1, sum * numList[x]);
DFS(x + 1, sum / numList[x]);
}
int main() {
for (int i = 0; i < 6; i++) {
char ch;
cin >> ch;
numList[i] = getNum(ch);
}
DFS(0, 0);
if(flag) cout << "YES";
else cout << "NO";
return 0;
}
1.2 动态规划(有问题,待优化)
思路
这里有几点需要注意:
1.首先注意这里判断可达时,需要使1.结果在0-24之间;2.运算是合法的(不得出现除0的情况)
2.这里为何需要一个临时数组存储信息?由于我们每次内循环的判断条件if (dp[j]),
如果每次出现一个符合的立即更新dp,那么下一次内循环也会出现符合,
但是我们希望的是基于一开始外循环提供的dp情况来进行,即比如像本来是通过 dp[0] -> dp[0+nums[0]],dp[0-nums[0]],dp[0*nums[0]],dp[0/nums[0]]
后面所有的j部分都因为!dp[j]而不可达,但是如果我们这里直接更新dp的话,部分dp就是可达的,导致规定的只进行一次运算并不能满足!!!
所以我们使用了一个临时数组存储相应信息,等到外循环结束,再来更新dp信息。
代码
#include <iostream>
using namespace std;
int nums[6];
int getNum(char ch) {
if (ch == 'A') return 1;
else if (ch == 'K') return 13;
else if (ch == 'Q') return 12;
else if (ch == 'J') return 11;
else return ch - '0';
}
int main() {
// 读取输入
for (int i = 0; i < 6; i++) {
char ch;
cin >> ch;
nums[i] = getNum(ch);
}
// 初始化动态规划数组
bool dp[43] = {false};
dp[0] = true;
// 动态更新动态规划数组
for (int i = 0; i < 6; i++) {
bool tmp[43] = {false}; // 临时数组存放当前牌的状态
for (int j = 0; j <= 42; j++) {
if (dp[j]) {
// 加法
if (j + nums[i] <= 42) tmp[j + nums[i]] = true;
// 减法
if (j - nums[i] >= 0) tmp[j - nums[i]] = true;
// 乘法
if (j * nums[i] <= 42) tmp[j * nums[i]] = true;
// 除法
if (nums[i] != 0) tmp[j / nums[i]] = true;
}
}
// 更新动态规划数组
for (int j = 0; j <= 42; j++) {
dp[j] = tmp[j];
}
}
// 输出结果
if (dp[42]) cout << "YES" << endl;
else cout << "NO" << endl;
return 0;
}
1.3 递推法
思路
用一个数组存储所有中间结果,利用中间结果不断递推即可。
代码
#include<bits/stdc++.h>
using namespace std;
bool flag = false;
int numList[6];
int getNum(char ch) {
if(ch == 'A') return 1;
else if (ch == 'K') return 13;
else if (ch == 'Q') return 12;
else if (ch == 'J') return 11;
else return ch - '0';
}
int main() {
for (int i = 0; i < 6; i++) {
char ch;
cin >> ch;
numList[i] = getNum(ch);
}
vector<int> ans[6];
ans[0].push_back(numList[0]);
for(int i = 1; i <= 5; i++) {
for(int num : ans[i-1]) {
ans[i].push_back(num + numList[i]);
ans[i].push_back(num - numList[i]);
ans[i].push_back(num * numList[i]);
ans[i].push_back(num / numList[i]);
}
}
for(int num : ans[5]){
if(num == 42){
cout << "YES";
return 0;
}
}
cout << "NO";
return 0;
}