前言:在编程问题中,取球游戏的一般解题模板
伪代码:
f(局面x){//——>胜(true)负(false)
边界条件处理。。。。。
For(我所有可能的走法){
试着走一步——>局面y
胜负t = f(y);
If(t==负)return 胜;
回溯,恢复局面
}
return 负;
}
今盒子里有n个小球,A、B两人轮流从盒中取球,每个人都可以看到另一个人取了多少个,
也可以看到盒中还剩下多少个,并且两人都很聪明,不会做出错误的判断。
我们约定:
每个人从盒子中取出的球的数目必须是:1,3,7或者8个。
轮到某一方取球时不能弃权!
A先取球,然后双方交替取球,直到取完。
被迫拿到最后一个球的一方为负方(输方)
请编程确定出在双方都不判断失误的情况下,对于特定的初始球数,A是否能赢?
程序运行时,从标准输入获得数据,其格式如下:
先是一个整数n(n<100),表示接下来有n个整数。然后是n个整数,每个占一行(整数<10000),表示初始球数。程序则输出n行,表示A的输赢情况(输为0,赢为1)。
例如,用户输入:
4
1
2
10
18
则程序应该输出:
1
1
代码:
publicclass 取球游戏 {
public static boolean f(int n){
if(n >= 1 &&f(n-1) == false ) return true;
if(n >=3 &&f(n-3) == false ) return true;
if(n >=7 &&f(n-7) == false ) return true;
if(n >=8 &&f(n-8) == false ) return true;
return false;
}
public static void main(String[] args){
System.out.println(f(10));//测试10个球
}
}
这个代码写的还是,不错的。通俗易懂,写的又漂亮!但是当球很多的时候,会超时有木有!!!
上面的算法:添加时间
import java.util.Scanner;
public class Test2 {
public static void main(String[] args) {
long begin = System.currentTimeMillis();
int a[] = {1,3,7,8};
Scanner sc = new Scanner(System.in);
int b = sc.nextInt();
System.out.println(f(a , b));//测试10个球
long end = System.currentTimeMillis();
System.out.println("运行时间是:"+(end-begin)/1000.0f);
}
public static boolean f(int a[], int n){
for(int i = 0 ; i < a.length ; i++){
if(n >= a[i] && f(a , n-a[i])==false ){
return true;
}
}
return false;
}
}
当输入的数为55 的时候,需要运行6s多,输入60时10s都出不来;这个是让人无法忍受的;
好了:下面用动态规划求解
import java.util.Scanner;
public class test {
static int it[] = new int[1000] ;//用一个数组来保持中间结果
public static void main(String[] args) {
int c[] = {1,3,7,8};
Scanner sc = new Scanner(System.in);
int b = sc.nextInt();
System.out.println(f(c , b));//测试10个球
}
//false == -1 ; true == 1 ; 0 --> 没有初始化;
public static int f(int a[] , int n){
if(it[n]!=0)return it[n];//如果在数组里面保存了这个值,马上返回
for(int i = 0 ; i < a.length ; i++){
if(n >= a[i] && f(a , n-a[i])==-1 ){
it[n] = 1;
return 1;
}
}
it[n] = -1;
return -1;
}
}
输入200 都很快出来了,再次体现了动态规划的优越性,有木有!
这个游戏除了胜负,还有平局的情况;
思路:
f(局面x)——>胜负平
{
Tag= 负;
for(所有可以走的位置)
{
试走一步——>局面y
结果t = f(y);
If(t==负)return 胜;
If(t == 平)tag = 平;
回溯,恢复局面
}
Returntag;
}
转载一下关于这个问题优秀的内容
Nim游戏的概述:
还记得这个游戏吗?
给出n列珍珠,两人轮流取珍珠,每次在某一列中取至少1颗珍珠,但不能在两列中取。最后拿光珍珠的人输。
后来,在一份资料上看到,这种游戏称为“拈(Nim)”。据说,它源自中国,经由被贩卖到美洲的奴工们外传。辛苦的工人们,在工作闲暇之余,用石头玩游戏以排遣寂寞。后来流传到高级人士,则用便士(Pennies),在酒吧柜台上玩。
最有名的玩法,是把十二枚便士放成3、4、5三列,拿光铜板的人赢。后来,大家发现,先取的人只要在3那列里取走2枚,变成了1、4、5,就能稳操胜券了,游戏也就变得无趣了。于是大家就增加列数,增加铜板的数量,这样就让人们有了毫无规律的感觉,不易于把握。
直到本世纪初,哈佛大学数学系副教授查理士•理昂纳德•包顿(Chales Leonard Bouton)提出一篇极详尽的分析和证明,利用数的二进制表示法,解答了这个游戏的一般法则。
一般规则是规定拿光铜板的人赢。
它的变体是规定拿光铜板的人输,只要注意某种特殊形态(只有1列不为1),就可以了!
有很多人把这个方法写成计算机程序,来和人对抗,不知就理的人被骗得团团转,无不惊叹计算机的神奇伟大。其实说穿了,只因为它计算比人快,数的转化为二进制其速度快得非人能比,如此罢了。
(以上来自K12教育论坛)
Nim游戏的数学理论论述:
Nim游戏是博弈论中最经典的模型,它又有着十分简单的规则和无比优美的结论
Nim游戏是组合游戏(Combinatorial Games)的一种,准确来说,属于“Impartial Combinatorial Games”(以下简称ICG)。满足以下条件的游戏是ICG(可能不太严谨):1、有两名选手;2、两名选手交替对游戏进行移动(move),每次一步,选手可以在(一般而言)有限的合法移动集合中任选一种进行移动;3、对于游戏的任何一种可能的局面,合法的移动集合只取决于这个局面本身,不取决于轮到哪名选手操作、以前的任何操作、骰子的点数或者其它什么因素; 4、如果轮到某名选手移动,且这个局面的合法的移动集合为空(也就是说此时无法进行移动),则这名选手负。根据这个定义,很多日常的游戏并非ICG。例如象棋就不满足条件3,因为红方只能移动红子,黑方只能移动黑子,合法的移动集合取决于轮到哪名选手操作。
通常的Nim游戏的定义是这样的:有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。
这游戏看上去有点复杂,先从简单情况开始研究吧。如果轮到你的时候,只剩下一堆石子,那么此时的必胜策略肯定是把这堆石子全部拿完一颗也不给对手剩,然后对手就输了。如果剩下两堆不相等的石子,必胜策略是通过取多的一堆的石子将两堆石子变得相等,以后如果对手在某一堆里拿若干颗,你就可以在另一堆中拿同样多的颗数,直至胜利。如果你面对的是两堆相等的石子,那么此时你是没有任何必胜策略的,反而对手可以遵循上面的策略保证必胜。如果是三堆石子……好像已经很难分析了,看来我们必须要借助一些其它好用的(最好是程式化的)分析方法了,或者说,我们最好能够设计出一种在有必胜策略时就能找到必胜策略的算法。
定义P-position和N-position,其中P代表Previous,N代表Next。直观的说,上一次move的人有必胜策略的局面是P-position,也就是“后手可保证必胜”或者“先手必败”,现在轮到move的人有必胜策略的局面是N-position,也就是“先手可保证必胜”。更严谨的定义是:1.无法进行任何移动的局面(也就是terminal position)是P-position;2.可以移动到P-position的局面是N-position;3.所有移动都导致N-position的局面是P-position。
按照这个定义,如果局面不可能重现,或者说positions的集合可以进行拓扑排序,那么每个position或者是P-position或者是N-position,而且可以通过定义计算出来。
以Nim游戏为例来进行一下计算。比如说我刚才说当只有两堆石子且两堆石子数量相等时后手有必胜策略,也就是这是一个P-position,下面我们依靠定义证明一下(3,3)是一个P是一个P是一个P-position。首先(3,3)的子局面(也就是通过合法移动可以导致的局面)有(0,3)(1,3)(2,3)(显然交换石子堆的位置不影响其性质,所以把(x,y)和(y,x)看成同一种局面),只需要计算出这三种局面的性质就可以了。 (0,3)的子局面有(0,0)、(0,1)、(0,2),其中(0,0)显然是P-position,所以(0,3)是N-position(只要找到一个是P-position的子局面就能说明是N-position)。(1,3)的后继中(1,1)是P-position(因为(1,1)的唯一子局面(0,1)是N-position),所以(1,3)也是N-position。同样可以证明(2,3)是N-position。所以(3,3)的所有子局面都是N-position,它就是P-position。通过一点简单的数学归纳,可以严格的证明“有两堆石子时的局面是P-position当且仅当这两堆石子的数目相等”。
根据上面这个过程,可以得到一个递归的算法——对于当前的局面,递归计算它的所有子局面的性质,如果存在某个子局面是P-position,那么向这个子局面的移动就是必胜策略。当然,可能你已经敏锐地看出有大量的重叠子问题,所以可以用DP或者记忆化搜索的方法以提高效率。但问题是,利用这个算法,对于某个Nim游戏的局面(a1,a2,...,an)来说,要想判断它的性质以及找出必胜策略,需要计算O(a1*a2*...*an)个局面的性质,不管怎样记忆化都无法降低这个时间复杂度。所以我们需要更高效的判断Nim游戏的局面的性质的方法。
直接说结论好了。
(Bouton's Theorem):对于一个Nim游戏的局面(a1,a2,...,an),它是P-position当且仅当a1^a2^...^an=0,其中^表示异或(xor)运算。
怎么样,是不是很神奇?我看到它的时候也觉得很神奇,完全没有道理的和异或运算扯上了关系。但这个定理的证明却也不复杂,基本上就是按照两种position的证明来的。
根据定义,证明一种判断position的性质的方法的正确性,只需证明三个命题: 1、这个判断将所有terminal position判为P-position;2、根据这个判断被判为N-position的局面一定可以移动到某个P-position;3、根据这个判断被判为P-position的局面无法移动到某个P-position。
第一个命题显然,terminal position只有一个,就是全0,异或仍然是0。
第二个命题,对于某个局面(a1,a2,...,an),若a1^a2^...^an!=0,一定存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。不妨设a1^a2^...^an=k,则一定存在某个ai,它的二进制表示在k的最高位上是1(否则k的最高位那个1是怎么得到的)。这时ai^k
第三个命题,对于某个局面(a1,a2,...,an),若a1^a2^...^an=0,一定不存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。因为异或运算满足消去率,由a1^a2^...^an=a1^a2^...^ai'^...^an可以得到ai=ai'。所以将ai改变成ai'不是一个合法的移动。证毕。
根据这个定理,我们可以在O(n)的时间内判断一个Nim的局面的性质,且如果它是N-position,也可以在O(n)的时间内找到所有的必胜策略。Nim问题就这样基本上完美的解决了。
(以上来自百度百科)
Nim游戏的形象具体论述:
Nim取子游戏是由两个人面对若干堆硬币(或石子)进行的游戏。设有k>=1堆硬币,各堆分别含有N1,N2,……NK枚硬币。游戏的目的就是选择最后剩下的硬币。游戏法则如下:
1.两个游戏人交替进行游戏(游戏人I和游戏人II);
2.当轮到每个游戏人取子时,选择这些堆中的一堆,并从所选的堆中取走至少一枚硬币(游戏人可以取走他所选堆中的全部硬币);
3.当所有的堆都变成空堆时,最后取子的游戏人即为胜者。
这个游戏中的变量是堆数k和各堆的硬币数N1,N2,……Nk。对应的组合问题是,确定游戏人I获胜还是游戏人II获胜以及两个游戏人应该如何取子才能保证自己获胜(获胜策略)。
为了进一步理解Nim取子游戏,我们考查某些特殊情况。如果游戏开始时只有一堆硬币,游戏人I则通过取走所有的硬币而获胜。现在设有2堆硬币,且硬币数量分别为N1和N2。游戏人取得胜利并不在于N1和N2的值具体是多少,而是取决于它们是否相等。设N1!=N2,游戏人I从大堆中取走的硬币使得两堆硬币数量相等,于是,游戏人I以后每次取子的数量与游戏人II相等而最终获胜。但是如果N1= N2,则:游戏人II只要按着游戏人I取子的数量在另一堆中取相等数量的硬币,最终获胜者将会是游戏人II。这样,两堆的取子获胜策略就已经找到了。
现在我们如何从两堆的取子策略扩展到任意堆数中呢?
首先来回忆一下,每个正整数都有对应的一个二进制数,例如:57(10) à 111001(2) ,即:57(10)=25+24+23+20。于是,我们可以认为每一堆硬币数由2的幂数的子堆组成。这样,含有57枚硬币大堆就能看成是分别由数量为25、24、23、20的各个子堆组成。
现在考虑各大堆大小分别为N1,N2,……Nk的一般的Nim取子游戏。将每一个数Ni表示为其二进制数(数的位数相等,不等时在前面补0):
N1 =as…a1a0
N2 =bs…b1b0
……
Nk =ms…m1m0
如果每一种大小的子堆的个数都是偶数,我们就称Nim取子游戏是平衡的,而对应位相加是偶数的称为平衡位,否则称为非平衡位。因此,Nim取子游戏是平衡的,当且仅当:
as +bs + … + ms 是偶数
……
a1 +b1 + … + m1 是偶数
a0 +b0 + … + m0是偶数
于是,我们就能得出获胜策略:
游戏人I能够在非平衡取子游戏中取胜,而游戏人II能够在平衡的取子游戏中取胜。
我们以一个两堆硬币的Nim取子游戏作为试验。设游戏开始时游戏处于非平衡状态。这样,游戏人I就能通过一种取子方式使得他取子后留给游戏人II的是一个平衡状态下的游戏,接着无论游戏人II如何取子,再留给游戏人I的一定是一个非平衡状态游戏,如此反复进行,当游戏人II在最后一次平衡状态下取子后,游戏人I便能一次性取走所有的硬币而获胜。而如果游戏开始时游戏牌平衡状态,那根据上述方式取子,最终游戏人II能获胜。
下面应用此获胜策略来考虑4-堆的Nim取子游戏。其中各堆的大小分别为7,9,12,15枚硬币。用二进制表示各数分别为:0111,1001,1100和1111。于是可得到如下一表:
23 = 8 |
22 = 4 |
21 = 2 |
20 = 1 |
|
大小为7的堆 |
0 |
1 |
1 |
1 |
大小为9的堆 |
1 |
0 |
0 |
1 |
大小为12的堆 |
1 |
1 |
0 |
0 |
大小为15的堆 |
1 |
1 |
1 |
1 |
由Nim取子游戏的平衡条件可知,此游戏是一个非平衡状态的取子游戏,因此,游戏人I在按获胜策略进行取子游戏下将一定能够取得最终的胜利。具体做法有多种,游戏人I可以从大小为12的堆中取走11枚硬币,使得游戏达到平衡(如下表),
23 = 8 |
22 = 4 |
21 = 2 |
20 = 1 |
|
大小为7的堆 |
0 |
1 |
1 |
1 |
大小为9的堆 |
1 |
0 |
0 |
1 |
大小为12的堆 |
0 |
0 |
0 |
1 |
大小为15的堆 |
1 |
1 |
1 |
1 |
之后,无论游戏人II如何取子,游戏人I在取子后仍使得游戏达到平衡。
同样的道理,游戏人I也可以选择大小为9的堆并取走5枚硬币而剩下4枚,或者,游戏人I从大小为15的堆中取走13枚而留下2枚。
归根结底,Nim取子游戏的关键在于游戏开始时游戏处于何种状态(平衡或非平衡)和第一个游戏人是否能够按照取子游戏的获胜策略来进行游戏。
(以上转自Rainco_shnu的百度空间)
下面写点自己的东西:
如果Nim游戏中的规则稍微变动一下,每次最多只能取K个,怎么处理?
方法是将每堆石子数mod (k+1).
例子:1 poj 1074
package 博弈;
importjava.util.Arrays;
importjava.util.Scanner;
public classPOJ1704
{
public staticvoid main(String[] args)
{
int t,n;
int[] num = newint[10005];
Scanner scan = newScanner(System.in);
t = scan.nextInt();
while((t--) !=0)
{
n =scan.nextInt();
for(int i = 1;i<= n;i++)
{
num[i] =scan.nextInt();
}
num[0] = 0;
Arrays.sort(num,0,n+ 1);
int ans = 0;
for(int i = n;i>= 1;i -= 2)
{
ans ^= (num[i] -num[i - 1] - 1);
}
if(ans == 0)
System.out.println("Bobwillwin");
else
System.out.println("Georgiawillwin");
}
}
}
如果Nim游戏中的规则稍微变动一下,每次最多只能取K个,怎么处理?
方法是将每堆石子数mod (k+1).
以上转载于: http://www.cnblogs.com/exponent/articles/2141477.html
下面写点自己的东西:
标题:高僧斗法
古时丧葬活动中经常请高僧做法事。仪式结束后,有时会有“高僧斗法”的趣味节目,以舒缓压抑的气氛。
节目大略步骤为:先用粮食(一般是稻米)在地上“画”出若干级台阶(表示N级浮屠)。又有若干小和尚随机地“站”在某个台阶上。最高一级台阶必须站人,其它任意。
两位参加游戏的法师分别指挥某个小和尚向上走任意多级的台阶,但会被站在高级台阶上的小和尚阻挡,不能越过。两个小和尚也不能站在同一台阶,也不能向低级台阶移动。
两法师轮流发出指令,最后所有小和尚必然会都挤在高段台阶,再也不能向上移动。轮到哪个法师指挥时无法继续移动,则游戏结束,该法师认输。
对于已知的台阶数和小和尚的分布位置,请你计算先发指令的法师该如何决策才能保证胜出。
输入数据为一行用空格分开的N个整数,表示小和尚的位置。台阶序号从1算起,所以最后一个小和尚的位置即是台阶的总数。(N<100, 台阶总数<1000)
输出为一行用空格分开的两个整数: A B, 表示把A位置的小和尚移动到B位置。若有多个解,输出A值较小的解,若无解则输出-1。
例如:
用户输入:
1 5 9
则程序输出:
1 4
再如:
用户输入:
1 5 8 10
则程序输出:
1 3
资源约定:
峰值内存消耗 < 64M
CPU消耗 < 1000ms
代码:
先用上面的暴力法,解决!
方法一:
public class Test{
public static boolean f(int[] x){
for(int i = 0; i for(int k = x[i]+1 ;k < x[i+1] ; k++){ int old =x[i]; x[i] = k; try{ if(f(x)== false) return true; }finally{ x[i]= old; } } } return false; } public static void main(String[] args)throws Exception{ /* //这是读取数据部分 BufferedReader bf = newBufferedReader(new InputStreamReader(System.in)); String[] str =bf.readLine().split(" "); int a[] = newint[str.length]; for(int i = 0 ; i a[i] = Integer.parseInt(str[i]); } */ int[] a ={1,3,5}; System.out.println( f( a) ); } } 方法二: public class Test{ static int count = 0; public static boolean f( int x[]){ for(int i = 0 ; i x[i] = x[i]+1;//加1 if(x[i] x[i] = x[i] -1;//回溯 } return false; } public static void main(String[] args){ int[] a ={1,3,5}; System.out.println( f( a) ); } } import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * * @author 寒星逸雪 * */ public class Test{ public static voidmain(String[] args)throws IOException{ int a[] = newint[101];//一个数组保存 小和尚的位置 int ans=0,len=0; BufferedReaderbf = new BufferedReader(new InputStreamReader(System.in)); String str[]=bf.readLine().split(" "); len =str.length; for(int j = 0 ;j < str.length; j++){ a[j] =Integer.parseInt(str[j]);//初始化 输入数据 } if(len%2 == 1)a[len]=a[len-1]+1; //如果输入的小和尚是 奇数,就再最高阶的位置后面再添加一个 小和尚。(使得,奇数偶数都一样处理) for(inti=0;i ans^=(a[i+1]-a[i]-1); //将0-1 , 2-3, 4-5 ,6-7,等之间的数进行异或运算 这些数之间 ,相当于小石堆 } if(ans != 0){ for(int i=0;i int k=ans^(a[i+1]-a[i]-1); if(a[i+1]-a[i]>k){ //缩短距离 为了达到 平衡位 System.out.println(a[i]+""+(a[i+1]-k-1)); break; } if(a[i+2]>k+a[i]+1){ //增加距离 当不能用缩短距离达到,平衡位时 System.out.println(a[i+1]+""+(a[i]+k+1)); break; } } } elseSystem.out.println(-1); //无解 } } 问题描述 闲暇时,福尔摩斯和华生玩一个游戏: 输入格式 输入数据为2行。第一行是若干空格分开的整数(每个整数介于1~100间),表示当前剩余的所有卡片。 输出格式 程序则输出必胜的招法!! 样例输入 2 3 6 样例输出 3 样例输入 1 2 2 3 3 4 5 样例输出 4 时间限制:1.0s 内存限制:256.0MB 声明: 这里我只是判断有没有解,答案是不对的,之所以也写在上面,是因为,我觉得这个解题思路很好,不喜勿喷! import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; public class NewBegin2 { // ArrayList /* f(全部的 , 可选的 ) if(没空可选的) return 负; for(遍历可选的){ 从全部的,和可选的中间移除 胜负 t = f(全部的,可选的) if(t== false) return true; } */ publicstatic void main(String[] args) throws IOException { //创建输入流 BufferedReaderbf = new BufferedReader(new InputStreamReader(System.in)); Stringstr1[] = bf.readLine().split(" ");//总共的 Stringstr2[] = bf.readLine().split(" ");//可选的 //用容器保存 ArrayList for(inti = 0 ; i < str1.length ; i++){ input.add(Integer.parseInt(str1[i])); } // for(inti = 0 ; i < str2.length ; i++){ // ArrayList // intch = Integer.parseInt(str2[i] ); System.out.println( f(input , 3 ) ); // } } publicstatic boolean f(ArrayList //1,移除 ch ArrayList i1.remove((Object)ch);//我走了第一步 //2,计算出还可以选择的 集合 ArrayList for(inti = 0 ; i < i1.size() ; i++){ inttemp = i1.get(i); if( temp%ch == 0 || ch%temp == 0 )able.add(temp); } //3.1,如果有可选的走法 , 试走一步 for(intj = 0 ; j < able.size() ; j++){ inttemp = able.get(j); booleant = f(i1 , temp); if(t == true) return false; } //3.2如果没有可以走的,那么对手就输了 returntrue; } } 下面是通过flag 的变化来做的,原理一样 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; public class NewBegin { publicstatic void main(String[] args) throws IOException { //创建输入流 BufferedReaderbf = new BufferedReader(new InputStreamReader(System.in)); Stringstr1[] = bf.readLine().split(" ");//总共的 Stringstr2[] = bf.readLine().split(" ");//可选的 //用容器保存 ArrayList for(inti = 0 ; i < str1.length ; i++){ input.add(Integer.parseInt(str1[i])); } for(inti = 0 ; i < str2.length ; i++){ ArrayList intch = Integer.parseInt(str2[i] ); System.out.println( f(i1 , ch , false) ); } } publicstatic boolean f(ArrayList //1,移除 ch ArrayList i1.remove((Object)ch);flag= !flag; //2,计算出还可以选择的 集合 ArrayList for(inti = 0 ; i < i1.size() ; i++){ inttemp = i1.get(i); if( temp%ch == 0 || ch%temp == 0 )able.add(temp); } //3,如果没有可选的结果,返回失败 if(able.size()== 0) return flag; //4,如果有可选的走法 , 试走一步 for(intj = 0 ; j < able.size() ; j++){ inttemp = able.get(j); booleant = f(i1 , temp ,flag); returnt; } return flag; } } 最终提交代码: 1、 把包名去掉 2、 把类名改了 3、 把中间的中文注释去掉 package 约数倍数选卡片; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; public class NewBegin3 { publicstatic void main(String[] args) throws IOException { //创建输入流 BufferedReaderbf = new BufferedReader(new InputStreamReader(System.in)); Stringstr1[] = bf.readLine().split(" ");//总共的 Stringstr2[] = bf.readLine().split(" ");//可选的 //用容器保存 ArrayList for(inti = 0 ; i < str1.length ; i++){ input.add(Integer.parseInt(str1[i])); } for(inti = 0 ; i < str2.length ; i++){ intch = Integer.parseInt(str2[i] ); booleant = f(input , ch) ; if(t== true){ System.out.println(ch); return; } } } publicstatic boolean f(ArrayList ArrayList //2,计算出还可以选择的 集合 ArrayList for(inti = 0 ; i < i1.size() ; i++){ inttemp = i1.get(i); if( temp%ch == 0 || ch%temp == 0 )able.add(temp); } if(able.size()== 1) return true; able.remove((Object)ch); i1.remove((Object)ch); //3.1,如果有可选的走法 , 试走一步 for(intj = 0 ; j < able.size() ; j++){ inttemp = able.get(j); booleant = f(i1 , temp); if(t == true) return false; } returntrue; } } 只有40分,意料之中,还是挺开心的,毕竟离成功有近了一步,明显是会超时。 好吧,没得说了,必须用动态规划 改进了一把,用ArrayList存储结构,好吧,还是只能过两组测试数据 package 约数卡片加动态规划; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; public class Main { staticArrayList publicstatic void main(String[] args) throws IOException { longa = System.currentTimeMillis(); //创建输入流 BufferedReaderbf = new BufferedReader(new InputStreamReader(System.in)); Stringstr1[] = bf.readLine().split(" ");//总共的 Stringstr2[] = bf.readLine().split(" ");//可选的 //用容器保存 ArrayList for(inti = 0 ; i < str1.length ; i++){ input.add(Integer.parseInt(str1[i])); } for(inti = 0 ; i < str2.length ; i++){ intch = Integer.parseInt(str2[i] ); booleant = f(input , ch) ; if(t== true){ System.out.println(ch); longb = System.currentTimeMillis(); System.out.println("运行时间:"+(b-a)/1000.0f); return; } } longb = System.currentTimeMillis(); System.out.println("运行时间:"+(b-a)/1000.0f); } publicstatic boolean bijiao(ArrayList if(a.size()!=b.size()){ returnfalse; } for(intx = 0 ; x < a.size();x++){ intm = a.get(x); if(b.contains((Object)m)== false) return false; } returntrue; } publicstatic boolean f(ArrayList if(al.size()!=0){ for(inti = 0 ; i < al.size() ; i++){ MyHelperhas = al.get(i); if(has.getCh()!= ch)break; ArrayList booleant = bijiao(input,isContain); if(t== true) return has.isT(); } } ArrayList //2,计算出还可以选择的 集合 ArrayList for(inti = 0 ; i < i1.size() ; i++){ inttemp = i1.get(i); if( temp%ch == 0 || ch%temp == 0 )able.add(temp); } if(able.size()== 1) return true; able.remove((Object)ch); i1.remove((Object)ch); //3.1,如果有可选的走法 , 试走一步 for(intj = 0 ; j < able.size() ; j++){ inttemp = able.get(j); booleant = f(i1 , temp); if(t == true) { MyHelpermy = new MyHelper(input , false , ch); al.add(my); returnfalse; } } MyHelpermy = new MyHelper(input , true , ch); al.add(my); returntrue; } } package 约数卡片加动态规划; import java.util.ArrayList; public class MyHelper { ArrayList booleant; intch ; publicMyHelper(){ } publicMyHelper(ArrayList this.al= al; this.t= t; this.ch= ch; } publicint getCh() { returnch; } publicvoid setCh(int ch) { this.ch= ch; } publicArrayList returnal; } publicvoid setAl(ArrayList this.al= al; } publicboolean isT() { returnt; } publicvoid setT(boolean t) { this.t= t; } } 好吧,估计只能用图来解决了; 待续。。。。。。。
分析:只要把 2*k -- 2*k+1 之间的数,看做小石堆就好!三、约数倍数选卡片
在N张卡片上写有N个整数。两人轮流拿走一张卡片。要求下一个人拿的数字一定是前一个人拿的数字的约数或倍数。例如,某次福尔摩斯拿走的卡片上写着数字“6”,则接下来华生可以拿的数字包括:
1,2,3,6,12,18,24 ....
当轮到某一方拿卡片时,没有满足要求的卡片可选,则该方为输方。
请你利用计算机的优势计算一下,在已知所有卡片上的数字和可选哪些数字的条件下,怎样选择才能保证必胜!
当选多个数字都可以必胜时,输出其中最小的数字。如果无论如何都会输,则输出-1。
第二行也是若干空格分开的整数,表示可以选的数字。当然,第二行的数字必须完全包含在第一行的数字中。
3 6
3 4 5