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;
}
posted @ 2024-04-09 16:49  DawnTraveler  阅读(31)  评论(0编辑  收藏  举报