问题描述
闲暇时,福尔摩斯和华生玩一个游戏:
在N张卡片上写有N个整数。两人轮流拿走一张卡片。要求下一个人拿的数字一定是前一个人拿的数字的约数或倍数。例如,某次福尔摩斯拿走的卡片上写着数字“6”,则接下来华生可以拿的数字包括:
1,2,3, 6,12,18,24 ....
当轮到某一方拿卡片时,没有满足要求的卡片可选,则该方为输方。
请你利用计算机的优势计算一下,在已知所有卡片上的数字和可选哪些数字的条件下,怎样选择才能保证必胜!
当选多个数字都可以必胜时,输出其中最小的数字。如果无论如何都会输,则输出-1。
输入格式
输入数据为2行。第一行是若干空格分开的整数(每个整数介于1~100间),表示当前剩余的所有卡片。
第二行也是若干空格分开的整数,表示可以选的数字。当然,第二行的数字必须完全包含在第一行的数字中。
输出格式
程序则输出必胜的招法!!
样例输入
2 3 6
3 6
样例输出
3
样例输入
1 2 2 3 3 4 5
3 4 5
样例输出
4
解析:
做了两次,一次0分,理解错了 ,第二次40分,一共5个测试用例,一个20分,后面两个超时了
第一次:
/**
*
* 分析:
* 1、依次判断选择每一个卡片是否必胜,最后取所有可能的最小值
* -----------------------------------------------------------------
* 2、判断选择该卡片是否必胜:
* 选择一张卡片
* 如果剩下的所有选择分支都是单数的话,那么选择该卡片是必胜的
* 如果出现一个分支是双数,则选择该卡片不是必胜的
*
* 3、判断选择该卡片是否必胜的步骤:
* (1)选择该卡片后,遍历剩下的所有卡片,找出所有符合下一次选择的卡片(也就是这一次选择卡片的约数和倍数)
* 卡片数0:则该选择必输
* 卡片数非0:重复步骤2,往下执行:
* -------------------------------------------------------------------
* 4、把虚线之间的分析封装为一个递归函数
* 参数:int 选择的卡片值,int 剩余选择数
* 结束递归条件:在剩下卡片中没有选择的卡片了
* 判断“剩余选择数”,是奇数返回true,偶数返回false
* 返回值:true表示必胜,false表示必输
*
*
*
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
public class Prev_17 {
//储存所有卡片
public static List<Integer> cards = new ArrayList<Integer>();
//最小必胜选项——默认值表示无论如何都会输
public static int victory = -1;
public static void main(String[] args) {
//输入两行数据
Scanner sc = new Scanner(System.in);
String firstLine = sc.nextLine();
String secondLine = sc.nextLine();
//把输入的第一行放入cards中
String[] split = firstLine.split(" ");
for(String s: split) {
cards.add(Integer.parseInt(s));
}
//把输入的第二行转换为数组
String[] split2 = secondLine.split(" ");
int[] options = new int[split2.length];
for(int i = 0; i < split2.length; i++) {
options[i] = Integer.parseInt(split2[i]);
}
//判断每一种选择是否正确
for(int i:options) {
cards.remove(new Integer(i));
//必胜
if(isVictory(i, 0)) {
if(victory == -1) {
victory = i;
} else {
victory = i < victory ? i : victory;
}
}
cards.add(i);
}
//最后输出最小必胜值
System.out.println(victory);
}
//递归判断该卡片是否是必胜选项
public static boolean isVictory(int cardValue,int count) {
//得到cardValue的所有公倍数和约数
int[] nextCards = new int[cards.size()];
int temp = 0;
for(Integer i:cards) {
if(i % cardValue == 0 || cardValue % i ==0) {
nextCards[temp++] = i;
}
}
nextCards = Arrays.copyOf(nextCards, temp);
//递归结束条件,cardValue是最后一张卡片
if(temp == 0) {
//是偶数则必胜
if(count % 2 == 0)
return true;
else
return false;
}
//
for(int i: nextCards) {
cards.remove(new Integer(i));
if(!isVictory(i,count + 1)) {
cards.add(i);
return false;
}
cards.add(i);
}
//运行到最后,表示每一种nextCard选择都必胜
return true;
}
}
第二次:
/**
*
*
*
* 分析:
* 1、循环判断每一种选择是否必胜,必胜则对比上一次必胜值,取跟小的值
* 2、最后输出最小值
*
*
* 判断一种选择是否必胜函数:
* dfs:
* 参数:int 我选的卡片
* 返回值Boolean
*
* 1、挑选出接下来对手可以选择的所有卡片
* 如果没有可选择的牌,表示这种情况我胜利(返回true)
* 如果有选择的牌,则:
* 2、循环判断对手选每一种卡片时,我是否有胜利的选择
* 循环前定义一个变量flag
*
* 循环体{
* 挑选出剩余的卡片中我可以选择的所有卡片:
* 如果没有可选择的牌,表示这种情况我输了(返回false)
* 如果有选择的牌,则:
* 循环判断选择每一种卡片是否可以取胜,只要有一种选择可以取胜,则:(循环前flag = false,直到找到可以胜利的可能是,赋值true)
* (1)flag = true表示这种情况可以胜利
* (2)跳出循环
*
* 判断flag,如果为false,则跳出循环,如果为true,继续
* }
*
* 3、返回flag
*
*
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
public class Prev_17_2 {
//储存所有卡片
public static List<Integer> cards = new ArrayList<Integer>();
//最小必胜选项——默认值表示无论如何都会输
public static int victory = -1;
public static void main(String[] args) {
//输入两行数据
Scanner sc = new Scanner(System.in);
String firstLine = sc.nextLine();
String secondLine = sc.nextLine();
//把输入的第一行放入cards中
String[] split = firstLine.split(" ");
for(String s: split) {
cards.add(Integer.parseInt(s));
}
//把输入的第二行转换为数组
String[] split2 = secondLine.split(" ");
int[] options = new int[split2.length];
for(int i = 0; i < split2.length; i++) {
options[i] = Integer.parseInt(split2[i]);
}
//判断每一种选择是否正确
Arrays.sort(options);
for(int i:options) {
cards.remove(new Integer(i));
//必胜
if(isVictory(i)) {
victory = i;
break;
/*if(victory == -1) {
victory = i;
} else {
victory = (i < victory ? i : victory);
}*/
}
cards.add(i);
}
//最后输出最小必胜值
System.out.println(victory);
}
//递归判断该卡片是否是必胜选项
public static boolean isVictory(int cardValue) {
//得到cardValue的所有公倍数和约数,也就是对手可以选择的所有可能性
int[] nextCards = new int[cards.size()];
int temp = 0;
for(Integer i:cards) {
if(i % cardValue == 0 || cardValue % i ==0) {
nextCards[temp++] = i;
}
}
nextCards = Arrays.copyOf(nextCards, temp);
//递归结束条件,cardValue是最后一张卡片
if(temp == 0) {
return true;
}
//循环判断对手选每一种卡片时,我是否有胜利的选择
boolean flag = true;
for(int i: nextCards) {
//对手选择卡片
cards.remove(new Integer(i));
//挑选出接下来我可以选择的所有卡片
int[] nextCards2 = new int[cards.size()];
int temp2 = 0;
for(Integer j:cards) {
if(j % i == 0 || i % j ==0) {
nextCards2[temp2++] = j;
}
}
nextCards2 = Arrays.copyOf(nextCards2, temp2);
//如果我没有卡片可以取,则输
if(temp2 == 0) {
//归还卡片
cards.add(i);
return false;
}
//找出是否有胜利的情况
flag = false;
for(int k: nextCards2) {
//取卡片
cards.remove(new Integer(k));
if(isVictory(k)) {
cards.add(k);
flag = true;
break;
}
//归还卡片
cards.add(k);
}
//归还卡片
cards.add(i);
if(!flag)
break;
}
//运行到最后,表示每一种nextCard选择都必胜
return flag;
}
}