现有21根火柴,两人轮流取,每人每次可以取1到4根,不可多取,也不能不取,谁取最后一根谁输。

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了现有21根火柴,两人轮流取,每人每次可以取1到4根,不可多取,也不能不取,谁取最后一根谁输。相关的知识,希望对你有一定的参考价值。

请编写一个程序进行人机对奕,要求人先取,计算机后取。计算机一方为常胜将军。
要求:
1)使用Turbo C 2.0完成基本功能
2)有较好的容错性
3)完整的注释
4)每一步都要有详细的说明
于今早11:30前作答

请高手出马!!
我要的是C的源程序及完整注释

你运行了吗

怎么老说#include<stdio.h> 有错误
s处

这个道理和编程无关,每人最多取4根,
1+4=5
21=5*4+1
也就是说,只要保证每轮两方之和是5,那么4轮后取走20根,最后先取的人必定取最后一根。

第二题:需要用递推的方式,计算所有必胜必输的状态,然后保证每次取火柴都让对方到达必输状态。
所谓必输就是只剩最后一根,或者无论怎么取后的结果都是必胜。
所谓必胜,就是可以对方到达必输状态的情况。

程序如下:
import java.io.*;

public class Picker
// 火柴堆的输赢状况
private final static int EMPTY=0; // 这种排列不可能出现,如108
private final static int UNKNOWN=1; // 尚未计算出
private final static int WIN=2; //必胜
private final static int LOSE=3; //必输(如果对方够聪明)

// 用数组,保存每种火柴堆排列的输赢状态,下标为排列,如357, 111, 001, 100等等
private int[] status;

public Picker()
// 初始化状态数组,排除所有不可能出现的情况
status = new int[358]; // 0 - 357
int i,j,k;
for (i=0; i<=3; i++)
for (j=0; j<=5; j++)
for (k=0; k<=7; k++)
status[i*100+j*10+k] = UNKNOWN;




// 已知 100, 010, 001必输
status[1]=LOSE;
status[10]=LOSE;
status[100]=LOSE;

// 所有能使对方到达上述三个状态的排列都是必赢的
markWin(1);
markWin(10);
markWin(100);

// 递推计算,直至357的情况被计算出来
while (status[357] == UNKNOWN)
//找到第一个没有计算的
int node=2;
for (node = 2; node<357; node++)
if (status[node] == UNKNOWN) break;

// 它的所有下个状态肯定都是必赢,不然以前就能算出。
status[node] = LOSE;
// 所有能使对方到达这个状态的排列都是必赢的
markWin(node);



// 所有能使下个状态变必输的排列都是必赢的
private void markWin(int node)
// 假设node为必输
// 每堆的数量分别为i,j,k
int i = node / 100;
int j = node / 10 % 10;
int k = node % 10;
// 先是第一堆,可能为i+1, i+2, ..., 3
for (int i1 = i+1; i1 <= 3; i1++)
status[i1*100+j*10+k] = WIN;

// 第二堆
for (int j1 = j+1; j1 <= 5; j1++)
status[i*100+j1*10+k] = WIN;

// 第三堆
for (int k1 = k+1; k1 <= 7; k1++)
status[i*100+j*10+k1] = WIN;




// 查找所有可能的一次取火柴后的排列,找出其中必输的状态,把这个作为自己的走法
public int getWinPick(int node)
//每堆的数量分别为i,j,k
int i = node / 100;
int j = node / 10 % 10;
int k = node % 10;
// 先是第一堆,可能留下0, 1, ..., i-1
for (int i1 = 0; i1 < i; i1++)
if (status[i1*100+j*10+k]==LOSE) return i1*100+j*10+k;

// 第二堆
for (int j1 = 0; j1 < j; j1++)
if (status[i*100+j1*10+k]==LOSE) return i*100+j1*10+k;

// 第三堆
for (int k1 = 0; k1 < k; k1++)
if (status[i*100+j*10+k1]==LOSE) return i*100+j*10+k1;

// 没有找到,那么先顽强抵抗一下,只取一根
if (i>0) return (i-1)*100+j*10+k;
else if (j>0) return (j-1)*10+k;
else return k-1;


public static void main(String[] args) throws Exception
Picker picker = new Picker();
// 一开始的排列是357
int node = 357;

BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));

while (node > 0)
// 计算机先
System.out.print("Now is "+node);
node = picker.getWinPick(node);
System.out.println(", I pick to "+node);
if (node == 0)
System.out.println("I lose");
return;

// 对方再取
System.out.println("Now is your turn: ");
String input = stdin.readLine();
int node1 = 0;
try
node1 = Integer.parseInt(input);
catch (Exception e)
System.out.println("Invalid input, you lose");
break;

// 这里没有判断取的是否合法,即node和node1之间是否仅差一位数字
if ( node1==0 )
System.out.println("You lose");

node = node1;




这个程序只是例子,是说明算法,没有判断输入的合法性,所以不能一直输入2的,人嘛,自己也遵循一下游戏规则吧。
另外,稍做改动,如果不利,计算机不会马上认输了。

这样看行不?
总结出,后走一方取子的数量与对方刚才一步取子的数量之和等于,就可以保证最后一个子是留给先取子的那个人的。
据此分析进行算法设计就是很简单的工作,编程实现也十分容易。

1+4=5
21=5*4+1
*程序与程序注释

#include<stdio.h>
void main()

int a=21,i;
printf("Game begin:\n");
while(a>0)

do
printf("How many stick do you wish to take(1~%d)?",a>4?4:a);
scanf("%d",&i);
while(i>4||i<1||i>a); /*接收正在确的输入*/
if(a-i>0) printf(" %d stick left in the pile.\n",a-i);
if((a-i)<=0)

printf(" You have taken the last stick.\n");
printf(" * * * You lose! \nGame Over.\n"); /*输出取胜标记*/
break;

else
printf(" Compute take %d stick.\n",5-i); /*输出计算机取的子数*/
a-=5;
printf(" %d stick left in the pile.\n",a);

参考技术A 哦 不错

均分火柴

题目

火柴厂生产火柴以后,要装盒。现有N个火柴盒要装填火柴,编号分别是 1,2,…,N。开始工人在每堆上都随意放了若干根火柴。但是一批次装填的火柴盒要求装填的根数一致,而且都必然能装进火柴盒。所以后续要求在任意盒里取若干根火柴左右移动。 移动火柴的规则为:在编号为1的盒上取的火柴,只能移动到编号为2的盒上;在编号为N的盒上取的火柴,只能移动到编号为N-1的盒上;其他盒的火柴,可以移动到相邻左边或右边的盒上。现在要求找出一种移动方法,用最少的移动次数使每盒上火柴数都一样多。 例如N=4,4盒火柴数分别为: ①9 ② 8 ③ 17 ④ 6 移动3次可达到目的: 从 ③ 取4根火柴放到④(9 8 13 10)->从③取3根火柴放到 ②(9 11 10 10)-> 从②取1根火柴放到①(10 10 10 10)。 答案要求在一秒钟之内,128MB内存的情况下得出。

输入格式:

输入文件中包括两行数据。 第一行为N个火柴盒的数值(1<=N<=100)。 第二行为N个火柴盒中每盒火柴初始数A1,A2,…,An(l<=Ai<=10000)。

输出格式:

输出文件中仅一行,即所有盒里火柴数均达到相等时的最少移动次数。

输入样例:

4
9 8 17 6

输出样例:

3

思路:

通过对输入样例的分析,得知我需要的就是将每堆火柴的数量增加或减少到总体的平均值。那么只需计算的得出平均值,然后遍历数组,从0号开始,若少于平均值则从下一堆中拿取,若多于平均值则放入下一堆,以此类推,一直到最后。

代码:

#include <cstdio>
int main()
{
    int num, sum = 0, avreage = 0, count = 0;
    int a[101] = { 0 };
    scanf("%d", &num);

    for (int i = 0; i < num; i++)
    {
        scanf("%d", &a[i]);
        sum += a[i];
    }

    avreage = sum / num;

    for (int i = 0; i < num; i++)
    {
        int temp = 0;
        if (a[i] != avreage)
        {
            temp = a[i] - avreage;
            a[i + 1] = a[i + 1] + temp;
            count++;
        }
    }
    printf("%d
", count);
    return 0;
}

以上是关于现有21根火柴,两人轮流取,每人每次可以取1到4根,不可多取,也不能不取,谁取最后一根谁输。的主要内容,如果未能解决你的问题,请参考以下文章

谁拿最后一根火柴 程序设计 大家帮帮忙!

C语言-常胜将军

python 实现21根火柴游戏

取火柴-博弈论

nim游戏解法(转)

尼姆博弈