Crosses and Crosses (Multi-SG+打表 博弈)

The game of Crosses and Crosses is played on the field of 1 × n cells. Two players make moves in turn. Each move the player selects any free cell on the field and puts a cross ‘×’ to it. If after the player’s move there are three crosses in a row, he wins.

You are given n. Find out who wins if both players play optimally.

Input

Input file contains one integer number n (3 ≤ n ≤ 2000).

Output

Output ‘1’ if the first player wins, or ‘2’ if the second player does.

Sample Input
       
#1 3
#2 6

Sample Output

       
#1 1
#2 2
 
 

这题需要好好叙述!!

题意:给你一个1*n长度的格子,两人轮流在格子上画"×",谁先画到“×××”谁赢,先手赢输出“1“ , 后手赢输出”2“。

第一种方法:用记忆化搜索来写。赢得的前一个状态是 ×0×   ××0   0××

因此不论先手放在哪个格子,它的前后两个格子后手都不会画上去。

这样就可以把N分成i-3和n-i-2两部分,再将其异或一下

下面贴上代码:

#include <stdio.h>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 2005;
int sg[maxn];
int getsg(int n)
{
	if(n < 0)
	{
		return 0;
	}
	if(sg[n] >= 0)
	{
		return sg[n];
	}
	int Hash[maxn];
	memset(Hash,0,sizeof(Hash));
	for(int i = 1 ; i <= n ; i++)
	{
		Hash[getsg(i-3) ^ getsg(n-i-2)] = 1;
	}
	for(int j = 0 ;;j++)
	{
		if(Hash[j] == 0)
		{
		return sg[n] = j;
		}
	}
}
int main()
{
	int n;
	memset(sg,-1,sizeof(sg));
	while(scanf("%d" , &n) !=EOF)
	{
		if(getsg(n))
		{
			printf("1\n");
		}
		else
		{
			printf("2\n");
		}
	}
	return 0;
}

第二种,先手推前面的初始值再打表,可以通过它推出规律,不过本题数据范围较小,可以直接打表。

sg[0]=0,sg[1]=1(mex(sg[-2]^sg[-2] ));sg[2]=1(mex(sg[-1]^sg[-1] ));sg[3]=1(mex(sg[0]^sg[0] ));

sg[4] = 2(mex(sg[0]^sg[1] , sg[0]^sg[0] ))同理sg[5] = 2;

打表代码:

void init()
{
    memset(sg,0,sizeof(sg));
    sg[0] = 0;
    sg[1] = 1;
    sg[2] = 1;
    sg[3] = 1;
    sg[4] = 2;
    sg[5] = 2;
    for(int i = 6 ; i < maxn ;i++)
    {
    
        memset(vis,0,sizeof(vis));
        for(int j = 3;j <= i;j++)
        {
            vis[sg[j-3]^sg[i-j-2]] = 1;
       }
        for(int j = 0 ;;j++)
        {
            if(vis[j]==0)
            {
                sg[i] = j;
                break;
            }
        }
    
         
    }
//     for(int i = 0 ; i<maxn ; i++)
//     {
//     	 cout<<"i:"<<i<<" sg[i]"<<sg[i]<<endl;
//     	 printf("SG[%d] = %d\n",i,sg[i]);
//	 }
}

这里有个超级莫名其妙的坑,单组输入才能过

int main()
{
	init();
    //函数打印规律
    int n;
    cin>>n;
    {
        if(sg[n] == 0)
         printf("2\n");
        else
         printf("1\n");
    }
    return 0;
}

另一种打表代码,找规律

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
const int maxn = 2005;
int sg[maxn];
int vis[maxn];
//先利用sg函数找到规律
void init()
{
    memset(sg,0,sizeof(sg));
    //初始状态
    sg[0] = 0;
    sg[1] = 1;
    sg[2] = 1;
    for(int i = 2 ; i < maxn ;i++)
    {
        memset(vis,0,sizeof(vis));
        //对于所有i状态,如果他的i-3/i-4/i-5状态是必败的话,那状态i就是必胜状态
        vis[sg[i-3]] = 1;
        vis[sg[i-4]] = 1;
        vis[sg[i-5]] = 1;
        for(int j = 0;j<= i-5;j++)
        {
            vis[sg[j]^sg[i-j-5]] = 1;
            //对于每一种sg值都遍历一遍
        }
        for(int j = 0 ;;j++)
        {
            if(!vis[j])//找出后继状态中mex(sg[x]);
            {
                sg[i] =j;
                break;
            }
        }
//        if(sg[i]==0)
      //  cout<<"i:"<<i<<" sg[i]"<<sg[i]<<endl;
    }
//     for(int i = 0 ; i<maxn ; i++)
//     {
//     	 printf("SG[%d] = %d\n",i,sg[i]);
//	 }
}
int main()
{
	
//	freopen("d://duipai//data.txt","r",stdin);
//	freopen("d://duipai//out1.txt","w",stdout);
    init();
    //函数打印规律
   // int k;
    int n;
    while(cin>>n)
    {
        if(sg[n]==0)
            cout<<"2"<<endl;
        else
            cout<<"1"<<endl;
    }
    return 0;
}




猜你喜欢

转载自blog.csdn.net/qq_41593380/article/details/80175419