题目描述
有这么一款单人卡牌游戏,牌面由颜色和数字组成,颜色为红、黄、蓝、绿中的一种,数字为0-9中的一个。游戏开始时玩家从手牌中选取一张卡牌打出,接下来如果玩家手中
有和他上一次打出的手牌颜色或者数字相同的手牌,他可以继续将该手牌打出,直至手牌打光或者没有符合条件可以继续打出的手牌。
现给定一副手牌,请找到最优的出牌策略,使打出的手牌最多。
输入描述
输入为两行,第一行是每张手牌的数字,数字由空格分隔,第二行为对应的每张手牌的颜色,用r y b g这4个字母分别代表4种颜色,字母也由空格分隔。手牌数量不超过10。
输出描述
输出一个数字,即最多能打出的手牌的数量。
用例
输入
json
1 4 3 4 5
r y b b r
输出
json
3
说明
json
如果打(1, r)-> (5, r),那么能打两张。
如果打(4,y) -> (4, b) -> (3, b),那么能打三张。
思考
暴力DFS。枚举每张牌作为打出的第一张牌,然后根据这张牌的数字和颜色在其余的牌中找匹配的继续打出,这个是递归搜索过程,可用 DFS 解决。需要注意:1)维护一个集合记录打出的牌,不要重复选相同的牌;2)每次选择一张牌 dfs 后需要回溯保证下次选择其它牌的前置状态正确。
算法过程
- 输入处理 :读取输入的两行数据,第一行是卡牌的数字数组
nums
,第二行是对应的卡牌颜色数组alphas
。 - 初始化 :定义全局变量
ans
用于记录最多能打出的卡牌数量,初始值为 0。 - 深度优先搜索(DFS) :
- 定义一个
used
集合来记录已经使用过的卡牌索引。 dfs
函数接收当前卡牌的索引index
和当前已经打出的卡牌数量count
。- 将当前卡牌标记为已使用,并增加
count
。 - 更新
ans
为当前count
和之前ans
的最大值。 - 遍历所有未被使用的卡牌,如果某张卡牌的数字或颜色与当前卡牌相同,则递归调用
dfs
继续搜索。 - 回溯时,将当前卡牌从
used
集合中移除,以便尝试其他路径。
- 定义一个
- 遍历所有起始卡牌 :对于每一张卡牌,作为起始卡牌调用
dfs
,确保找到所有可能的出牌路径中的最大值。 - 输出结果 :最终输出
ans
,即最多能打出的卡牌数量。 - 时空复杂度分析 :
- 时间复杂度 :由于手牌数量不超过 10,最坏情况下需要遍历所有可能的出牌顺序。对于每张卡牌作为起始点,DFS 的递归深度最多为手牌数量
n
,每次递归需要遍历剩余未使用的卡牌,因此时间复杂度为O(n!)
,其中n
是手牌数量。由于n ≤ 10
,实际运行时间是可接受的。 - 空间复杂度 :主要空间消耗来自递归调用栈和
used
集合。递归深度最多为n
,used
集合最多存储n
个元素,因此空间复杂度为O(n)
。
- 时间复杂度 :由于手牌数量不超过 10,最坏情况下需要遍历所有可能的出牌顺序。对于每张卡牌作为起始点,DFS 的递归深度最多为手牌数量
参考代码
cpp
function solution() {
const nums = readline().split(' ').map(Number);
const alphas = readline().split(' ').map(Number);
let ans = 0;
const used = new Set();
const dfs = function(index, count) {
used.add(index);
count++;
ans = Math.max(ans, count);
for (let i = 0; i < nums.length; i++) {
if ( used.has(i) || nums[i] !== nums[index] && alphas[i] !== alphas[index]) continue;
dfs(i, count);
used.delete(i);
}
};
for (let i = 0; i < nums.length; i++) {
used.clear();
dfs(i, 1);
}
console.log(ans);
}
const cases = [
`1 4 3 4 5
r y b b r`
];
let caseIndex = 0;
let lineIndex = 0;
const readline = (function () {
let lines = [];
return function () {
if (lineIndex === 0) {
lines = cases[caseIndex]
.trim()
.split("\n")
.map((line) => line.trim());
}
return lines[lineIndex++];
};
})();
cases.forEach((_, i) => {
caseIndex = i;
lineIndex = 0;
solution();
});