题目描述
有这么一款单人卡牌游戏,排面由颜色和数字组成,颜色为红、黄、蓝、绿中的一种,数字为0-9中的一个。游戏开始时玩家从手牌中选取一张卡牌打出,接下来如果玩家手中有和他上一次打出的授牌颜色或者数字相同的手牌,他可以继续讲该手牌打出,直至手牌灯光或者没有符合条件可以继续打出的手牌。现给定一副手牌,请找出最优的出牌策略,使打出的手牌最多。
输入描述:
输入为两行,第一行是每张手牌的数字,数字由空格分割,第二张为对应的每张手牌的颜色,用r y b g这4个字母分别代表4中颜色,字母也由空格分割。手牌数量不超过10。
输出描述:
输出一个数字,即最多能打出的手牌的数量。
示例1
输入
1 4 3 4 5
r y b b r
输出
3
说明
如果打出1r,那么下面只能再打出5r,共打出两张牌,而按照4y-4b-3b的顺序则可以打出三张牌,故输出3
示例2
输入
1 2 3 4 r y b l
输出
1
说明
没有能够连续出牌的组合,只能在开始时打出一张手牌,故输出1
解题思路:
看到这种需要轮询便利的场景就要想到递归
1、将输入的牌作为数组([数字,颜色])放入结合中
2、遍历集合,循环取出其中一张牌
3、将取出的这种牌与其他牌进行比较,如果出现数字相同或者颜色相同的牌,就讲其取出,重复步骤3,直至遍历到最后一张牌也没有符合条件的为止(递归边界)
。
注意:
没取出一张牌,都需要从集合中剔除次牌,且出牌次数需要加一。 此题最需要注意的就是,每次递归的时候,都需要复制一次集合,防止出牌的时候影响之后的手牌
public class Main{
public static int max; //最大出牌数量
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String[] str1 = sc.nextLine().split(" ");
String[] str2 = sc.nextLine().split(" ");
List<String[]> list = new ArrayList<>(); //卡牌集合
int len = str1.length; //卡牌数量
for(int i=0;i<len;i++){
String[] strings = new String[2];
strings[0] = str1[i];
strings[1] = str2[i];
list.add(strings); //将每张卡牌的数字和颜色作为数组放入集合中
}
for(int i=0;i<len;i++){
List<String[]> copyList = copy(list); //需要备份集合,集合内容会受到上次的操作影响
copyList.remove(i); //将索引为i的牌取出
chupai(list.get(i), copyList, 1); //出牌数量count,初始化为1
}
System.out.println(max);
}
public static List<String[]> copy(List<String[]> list){
int len = list.size();
List<String[]> res = new ArrayList<>();
for(int i=0;i<len;i++){
res.add(list.get(i));
}
return res;
}
/**
*
* @param str 此时用来做对比的牌
* @param list 此时手上的牌(除去出过的牌)
* @param count 出牌次数
*/
public static void chupai(String[] str, List<String[]> list, int count){
for(int i=0;i<list.size();i++){
String[] strings = list.get(i);
if(strings[0].equals(str[0]) || strings[1].equals(str[1])){ //如果数字相同或者颜色相同则进入下轮出牌
List<String[]> temp = copy(list);
temp.remove(i);
if(temp.size()==0){ //手上的牌出完了
max = Math.max(max, count+1);
break;
}
chupai(strings, temp, count+1);
}else if(i==list.size()-1){ //遍历到最后一张都没有可以出的牌
max = Math.max(max, count);
}
}
}
}