java:约数倍数选卡片(博弈论)

java:约数倍数选卡片(博弈论)

问题描述
  闲暇时,福尔摩斯和华生玩一个游戏:
  在N张卡片上写有N个整数。两人轮流拿走一张卡片。要求下一个人拿的数字一定是前一个人拿的数字的约数或倍数。例如,某次福尔摩斯拿走的卡片上写着数字“6”,则接下来华生可以拿的数字包括:
  123, 6121824 ....
  当轮到某一方拿卡片时,没有满足要求的卡片可选,则该方为输方。
  请你利用计算机的优势计算一下,在已知所有卡片上的数字和可选哪些数字的条件下,怎样选择才能保证必胜!
  当选多个数字都可以必胜时,输出其中最小的数字。如果无论如何都会输,则输出-1。
输入格式
  输入数据为2行。第一行是若干空格分开的整数(每个整数介于1~100间),表示当前剩余的所有卡片。
  第二行也是若干空格分开的整数,表示可以选的数字。当然,第二行的数字必须完全包含在第一行的数字中。
输出格式
  程序则输出必胜的招法!!
样例输入
2 3 6
3 6
样例输出
3
样例输入
1 2 2 3 3 4 5
3 4 5
样例输出
4

这个我并没有看懂

import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

public class 约数倍数选卡片 {
     
	static int numCard[] =new int[101];// 下标为代表数字,值为数字出现次数
	static ArrayList<Integer>[] listA=new ArrayList[101];// 存储每个数的约数倍数
	static ArrayList<Integer> listB = new ArrayList<Integer>();// 存储可选数
	
	
	public static void main(String[] args) {
     
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		String A=sc.nextLine();
		String B=sc.nextLine();
		sc.close();
		String[] a=A.split("\\s+");// 空格个数不一定是一个,直接用" "分隔会错误50%
		String[] b=B.split("\\s+");
		
		for(int i=0;i<a.length;i++){
     
			int num=Integer.parseInt(a[i]);
			numCard[num]+=1;//记录数字出现的次数
		}
		for(int i=0;i<b.length;i++)
			listB.add(Integer.parseInt(b[i]));//记录可选数字
		Collections.sort(listB);//对可选数字排序,这样有多种可行必胜策略时,只会输出较小数
		for(int i=0;i<101;i++)
			listA[i]=new ArrayList<Integer>();//实例化链表,用于存储数字的约数倍数
		
		for(int i=1;i<101;i++){
     //遍历1~100
			if(numCard[i]!=0){
     //如果该数存在
				numCard[i]--;//查看该数被取走后他的约数倍数是否存在
				for(int j=1;j<101;j++){
     //如果该数的约数倍数存在
					if (numCard[j] != 0 && (j % i == 0 || i % j == 0)) {
     
						listA[i].add(j);//存入
					}
				}
				numCard[i]++;//回溯
			}
		}
		for (int i = 0; i < listB.size(); i++) {
     //遍历可选数
			int num = listB.get(i);
			numCard[num]--;//取出该数
			boolean result = dfs(num);//让对方选
			numCard[num]++;//回溯
			if (!result) {
     //对手必败,即我必胜
				System.out.println(num);
				return;
			}
		}
		System.out.println(-1);//执行自此说明我取那一张都不行,必败无疑
	}
	private static boolean dfs(int num) {
     
		//遍历该数的约数倍数,优先选取较大值,因为100以内大数的约数倍数更少,胜率更高,减少递归层数
		//如果从i从0开始会超时33%
		for (int i = listA[num].size() - 1; i >= 0; i--) {
     
			int num2 = listA[num].get(i);
			if (numCard[num2] > 0) {
     //如果该数存在约数倍数
				numCard[num2]--;//取出
				boolean result = dfs(num2);//轮到我选(这一步会有多层递归,双方轮流选,直到一方没得选)
				numCard[num2]++;//回溯
				if (!result ) {
     //自己选的时候不存在,也就是我没得选了,对方胜利
					return true;
				}
			}
		}
		return false;//该数的约数倍数不存在
	}
}

你可能感兴趣的:(蓝桥)