Pku 1143:

Description

Christine and Matt are playing an exciting game they just invented: the Number Game. The rules of this game are as follows. The players take turns choosing integers greater than 1. First, Christine chooses a number, then Matt chooses a number, then Christine again, and so on. The following rules restrict how new numbers may be chosen by the two players:

  • A number which has already been selected by Christine or Matt, or a multiple of such a number,cannot be chosen.
  • A sum of such multiples cannot be chosen, either.

If a player cannot choose any new number according to these rules, then that player loses the game. Here is an example: Christine starts by choosing 4. This prevents Matt from choosing 4, 8, 12, etc.Let's assume that his move is 3. Now the numbers 3, 6, 9, etc. are excluded, too; furthermore, numbers like: 7 = 3+4;10 = 2*3+4;11 = 3+2*4;13 = 3*3+4;... are also not available. So, in fact, the only numbers left are 2 and 5. Christine now selects 2. Since 5=2+3 is now forbidden, she wins because there is no number left for Matt to choose. Your task is to write a program which will help play (and win!) the Number Game. Of course, there might be an infinite number of choices for a player, so it may not be easy to find the best move among these possibilities. But after playing for some time, the number of remaining choices becomes finite, and that is the point where your program can help. Given a game position (a list of numbers which are not yet forbidden), your program should output all winning moves. A winning move is a move by which the player who is about to move can force a win, no matter what the other player will do afterwards. More formally, a winning move can be defined as follows.

  • A winning move is a move after which the game position is a losing position.
  • A winning position is a position in which a winning move exists. A losing position is a position in which no winning move exists.
  • In particular, the position in which all numbers are forbidden is a losing position. (This makes sense since the player who would have to move in that case loses the game.)

Input

The input consists of several test cases. Each test case is given by exactly one line describing one position. Each line will start with a number n (1 <= n <= 20), the number of integers which are still available. The remainder of this line contains the list of these numbers a1;...;an(2 <= ai <= 20). The positions described in this way will always be positions which can really occur in the actual Number Game. For example, if 3 is not in the list of allowed numbers, 6 is not in the list, either. At the end of the input, there will be a line containing only a zero (instead of n); this line should not be processed.

Output

For each test case, your program should output "Test case #m", where m is the number of the test case (starting with 1). Follow this by either "There's no winning move." if this is true for the position described in the input file, or "The winning moves are: w1 w2 ... wk" where the wi are all winning moves in this position, satisfying wi < wi+1 for 1 <= i < k. After this line, output a blank line.

题目大意:两个人玩零和博弈游戏,轮流选取[2,20]的整数,最终没有数字可选的玩家输。而数字无法被选有如下的两条限制:

(1)    选过的数字不能再选,其整数倍的数字也不能再选。

(2)    不能选的数字的和不能被选择。

现在给出游戏过程中某个某个状态<i,j,k…>表示当前i、j、k…还可以选,那么请编程输出当前状态下的所有必胜策略。如果不存在,则输出对应的语句。

分析:其实这道题和之前我们探讨过的一道博弈问题非常的类似,可以说在整体的思维上是一致的。我们从特殊的情形来分析然后逐步进行推广。如果给出的状态是<2,4>,那么显然必胜数(就是当前状态游戏选择该数会引导出必胜策略,下面都简称为必胜数)是2。如果给出的状态是<2,3,4>,显然必胜数是4。很容易看到,对于小规模的状态时,我们非常容易判断,那么对于较大规模的状态,我们依据前后状态的转移关系(一个是状态参数关系的转移,另一个则是博弈NP态的转移),可以通过递归缩减状态规模的方法来判断当前状态是否存在必胜态。

以上是对于这道问题一个整体思维的分析,下面我们面临的问题是如何编程来模拟实现这个基于递归的计算过程。

数据结构:

首先我们需要一种结构来记录某种状态<i,j,k…>,为了编程的方便,我们用一个整数num来记录,但是我们从二进制的角度去解读这个整数num,举个例子来说,对于状态<2,3,4>,即表示二进制数从右边开始,第2、3、4位是1,即有num = 14.

其次我们看到这个递归搜索的过程可能在后面输入的时候进行重复计算,因此这里应该采用记忆化搜索的策略,这里我们用map[num]来表示状态num(基于上一段我们说过的转化)的胜负态。

状态转移:

状态参数之间的转移:假设这里我们给出了一个状态num0,<a1,a2,a3…>,现在我们需要做的是依次取走a1、a2、a3…所形成的状态num1、num2、num3中的胜负态是怎样的。那么这里我们面临这样一个问题,从num0出发,我们如何得到num1、num2、num3这些状态参数呢?我们当然要从题目要求当中提取信息——取走a1会导致a1的整数倍、a1和其余不可取的数字的和变成不可取,这便是状态转移的关键。

博弈NP态的转移:这一点是非常重要的,因为我们最终要看的就是这种状态到底存不存在必胜策略,这里我们需要知道的是,从一个大规模问题递归搜索胜负态的时候,对于某条状态路径num0->num1->num2…,其胜负态一定是交替的,即N->P->N->P…这一点基本对于所有的二元零和博弈都适用。

进一步基于编程化的思考:

那么通过上面的分析,我们看到一个整体渐渐清晰的思维轮廓,基于状态参数之间的转移关系和二元博弈自身胜负态的转移关系,我们能够实现递归求解,但是在具体的编程中,我们应该如何实现呢?

一个最困难的点就是状态参数之间的转移,这里我们要运用到巧妙的位运算,首先假设一个问题情境,对于状态num0,我们拿走整数i,将生成的状态用num1记录。我们用j记录i的整数倍,那么num0 | 1 << j所表示的二进制数将能够表征状态num1所有不能取的数字,即如果数字k在状态num1中不能取,则num1的二进制表示中自右往左第k+1位是0.下面我们要做的应该是保留那些能够取的数字,我们通过一个整数2^(j-1) – 1来得到状态num0中小于j的数字,然后二者做‘|’运算,在于num0做‘&’运算,即可得到num1.

即num1 = num0 & ((num0 | 1 << j)| 2^(j-1) - 1),这是这道题的状态参数转移式字。

而对于NP态的转移方程,设map[num]记录状态num的胜负态(map[num] = 1表示面临当前状态的玩家有必胜态,否则map[num] = 0),num1,num2,num3…numi是num0的下一个状态,则有如下的胜负态转移方程:

map[num0] = (1 - map[num1]) | (1 – map[num2]) |(1 – map[num3] ) | … |(1 – map[numi]).

基于这个层面的分析,便可以编程实现了。

简单的参考代码如下:

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = ;//总状态数 int Map[maxn];
int mask[];
int judge(int num)
{
int i , j;
if(Map[num] == -)
{
Map[num] = ;
for(i = ;i <= ;i++)//遍历当前状态可能取走数的所有情况
{
if((num & mask[i]))//整数i在当前状态可以取
{
int temp = num;
j = i;
while(j <= )
{
temp &= (((num | ) <<j) | (mask[j] - )); //位运算,<<优先于 | ,这里应该加小括号
j += i;
} int Win = - judge(temp);
if(Win == )
Map[num] += mask[i];
}
}
} return Map[num];
} int main()
{
mask[] = ;
Map[] = ;
for(int i = ;i < ;i++)
mask[i + ] = << i;
memset(Map,-,sizeof(Map));
int n;
int tt = ;
while(scanf("%d",&n) != EOF)
{
if(n == ) break; int a = ;
for(int i = ;i <= n;i++)
{
int temp;
scanf("%d",&temp);
a |= mask[temp];//对于初始状态的重构
}
printf("Test Case #%d\n",tt++);
int answer = judge(a);
if(answer == )
printf("There's no winning move.\n");
else
{
printf("The winning moves are:");
answer /= ;
for(int i = ;i <= ;i++)
{
if(answer% == )
printf(" %d",i); answer >>= ;
if(answer == )
break;
}
printf("\n");
}
printf("\n");
}
}

《ACM国际大学生程序设计竞赛题解I》——6.10的更多相关文章

  1. 《ACM国际大学生程序设计竞赛题解Ⅰ》——基础编程题

    这个专栏开始介绍一些<ACM国际大学生程序设计竞赛题解>上的竞赛题目,读者可以配合zju/poj/uva的在线测评系统提交代码(今天zoj貌似崩了). 其实看书名也能看出来这本书的思路,就 ...

  2. 《ACM国际大学生程序设计竞赛题解I》——6.11

    pku 1107: Description Weird Wally's Wireless Widgets, Inc. manufactures an eclectic assortment of sm ...

  3. 《ACM国际大学生程序设计竞赛题解I》——6.8

    Poj1068: Description Let S = s1 s2...s2n be a well-formed string of parentheses. S can be encoded in ...

  4. 《ACM国际大学生程序设计竞赛题解Ⅰ》——模拟题

    这篇文章来介绍一些模拟题,即一类按照题目要求将现实的操作转换成程序语言. zoj1003: On every June 1st, the Children's Day, there will be a ...

  5. 2018 ACM 国际大学生程序设计竞赛上海大都会部分题解

    题目链接 2018 ACM 国际大学生程序设计竞赛上海大都会 下午午休起床被同学叫去打比赛233 然后已经过了2.5h了 先挑过得多的做了 .... A题 rand x*n 次点,每次judge一个点 ...

  6. 2018 ACM 国际大学生程序设计竞赛上海大都会赛

    传送门:2018 ACM 国际大学生程序设计竞赛上海大都会赛 2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛2018-08-05 12:00:00 至 2018-08-05 17:00:0 ...

  7. 2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 F Color it

    链接:https://www.nowcoder.com/acm/contest/163/F 来源:牛客网 2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 F Color it 时间限制:C ...

  8. 2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 F Color it (扫描线)

    2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 F Color it (扫描线) 链接:https://ac.nowcoder.com/acm/contest/163/F来源:牛客网 时间 ...

  9. 2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 J Beautiful Numbers (数位DP)

    2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 J Beautiful Numbers (数位DP) 链接:https://ac.nowcoder.com/acm/contest/163/ ...

随机推荐

  1. (转)ecshop刷新页面出现power by ecshop和链接的解决办法

    当小伙伴在使用echop模板进行修改的时候,如果你删掉底部自带版权后,再调试程序刷新界面的时候,时不时就会冒出一个power by ecshop,而且是带有链接的,很不舒服,所以需要去掉,下面是最简单 ...

  2. [转载]CentOS6.4+Mono3.0.7+Jexus5.2.5

    本文章来自互联网,但是本人已经在VM虚拟机里面测试成功,所以分享给大家 1.更新 yum -y update 2.安装Mono源码安装需要的库 yum -y install gcc gcc-c++ a ...

  3. powerdesigner设置唯一键,但不是主键的方式

    [转载]http://blog.csdn.net/cnham/article/details/6676650 唯一约束 唯一约束与创建唯一索引基本上是一回事,因为在创建唯一约束的时候,系统会创建对应的 ...

  4. IIViewDeckController的使用,左右拖拉菜单效果实现

    博客园   IIViewDeckController的使用,左右拖拉菜单效果实现   很多应用里面都实现了对应的侧拉 显示隐藏的效果,看起来很符合用户体验的类似于这种   看起来很好看,今天去晚上搜下 ...

  5. ROW_NUMBER分页的注意事项

    之前在使用ROW_NUMBER分页获取数据的时候,直接用ROW_NUMBER里的SELECT语句查出了所有的数据. like this: select * from ( select row_numb ...

  6. PHP框架_ThinkPHP数据库

    目录 1.ThinkPHP数据库配置 2.ThinkPHP数据库实例化模型 3.ThinkPHP数据库CURD操作 4.ThinkPHP数据库连贯操作 1.ThinkPHP数据库配置 App/Conf ...

  7. dedecms设置文章分页后,标题会带有序号的解决方法

    至于删除分页后标题后面的序号,找到include/arc.archives.class.php 打开,找到 if($i>1) $this->Fields['title'] = $this- ...

  8. Trigger model Trigger expr_id in WorkFolow

    For example, suppose you want to set a Sale Order into the state "Done" once it has been s ...

  9. 修改app名字

    一张图说明问题 如果没有成功clean一下,或者卸载掉原有的重新生成一下 如果要修改路径名和工程名有个复杂的方法 http://blog.sina.com.cn/s/blog_a42013280101 ...

  10. readline函数分析

    函数功能:提示用户输入命令,并读取命令/****************************************************************************/ /* ...