约数倍数选卡片

问题描述

  闲暇时,福尔摩斯和华生玩一个游戏:
  在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;
	}
	
	
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值