菜鸟学习ACM,纪录自己成长过程中的点滴。

学习的路上,与君共勉。

ACM学习-POJ-1143-Number Game

Number Game
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 2914   Accepted: 1162

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.

Sample Input

2 2 5
2 2 3
5 2 3 4 5 6
0

Sample Output

Test Case #1
The winning moves are: 2 Test Case #2
There's no winning move. Test Case #3
The winning moves are: 4 5 6

Source

题目要求:

Christine和Matt玩一个游戏.游戏的规则如下:一开始有一列数字(2~20),有的被列出,有的没有列出.从Christine开始,两人轮流划去一个已被列出的数字.每划去一个数字,其他的所有的能被不在这个数列的数字的两两整线性表示的数也自动从数列中退出.到最后,轮到某人而此时数列中没有元素时,这个人就输了.

       规定:winning moveà能将对手置于losing position的决策,而losing positionà不存在任何winning move的序列.

这两个概念都是在两人都play perfectly 的前提下提出的.

题目分析:

这是一道博弈题,既然要判断先选哪个数一定获胜,那么可首先枚举要选的第一个数,删除该数影响到的数。

那么什么数(设为x)会受该数(设为m)影响呢?分析可知,条件是x-m不在当前可选数列中,也就是说x-m已经选过。

而又选了m,那么由题意可知x必定受到影响要删除。

条件已知。那么剩下的就是根据必胜状态和必败状态的定义搜索这棵博弈树。直接搜索超时,已经试过了。

在网上搜了下,都没有注释。于是找了个代码,啃啊啃,终于明白了。

用的是记忆化搜索。将已给序列从小到大排序。如果用1表示该数在序列中,0表示不在,那么就用它们对应的二进制数来表示

当前面临的状态,如果面临的状态相同且前面已经得出,那么直接返回该结果即可。需要用的两个二进制数,一个用来表示状

态,另一个用来表示该状态下剩余的数(其中1表示不在可选序列中,0表示在)。这样做在删除数的时候非常方便。

下面给出AC代码。

#include<stdio.h>
#include<string.h> int dp[1<<20];
int info[21];
int n; // 每组的个数 int solve(int a,int b)
{
int i,j,c,d;
if( a == 0 )
return 0;
if( dp[a] != -1 ) //表明已经判断过了。 直接返回结果
return dp[a];
for( i = 0; i < n; i ++ ){
c = a;
d = b;
if( (1 << i) & c ){ //寻找该状态下可选的数,继续搜索
for( j = 0; j + info[i] < 21; j ++ ){ //标记受该数影响的数
if( (1 << j) & d ){
d = d | (1 << (info[i] + j) );
}
}
for( j = 0; j < n; j ++ ){ //删除受影响的数
if( ( (1 << j ) & c ) && ( (1 << info[j]) & d ) ){
c = c^(1 << j);
}
}
if( !solve(c,d) )
return dp[a] = 1;
}
}
return dp[a] = 0;
} int main()
{
int i, j, k;
int a,b; //a表示状态,初始全为1 b表示序列删除的状况,1表示不在,0表示在
int item; // 总的序列。 0表示存在,1表示不在
int res[21];
int case_ = 1; //记录测试组数
while(scanf("%d",&n) != EOF && n!= 0)
{
item = (1<<22) - 3; //将1添进序列,1始终是可选但又不能选的数。总序列变成 11 1111 1111 1111 1111 1101 0指该序列中0存在。
for( i = 0 ; i < n; i ++ ){
scanf("%d", &info[i]);
item ^= (1 << info[i]); //^异或运算符,位值相同为0,不同为1。将序列中的数在item中标记,相应位置都为0,这样做删除方便
}
for( i = 0; i < n; i ++ ){ //冒泡排序
for( j = 0; j < n - i - 1; j ++ ){
if( info[j] > info[j + 1] ) {
info[j] = info[j] ^ info[j + 1];
info[j + 1] = info[j] ^ info[j + 1];
info[j] = info[j] ^ info[j + 1];
}
}
}
memset(dp,-1,sizeof(dp));
for(k = i = 0; i < n;i ++ ){ //枚举第一个被选的数
a = (1<<n) - 1; //a表示状态,初始全为1
b = item; //b表示序列删除的状况,1表示不在,0表示在
for( j = 0; j + info[i] < 21; j ++ ){
if( (1<<j) & b ){ //如果j不在可选序列,那么j + info[i] 会因info[i]的删除而删除
b = b | ( 1 << (j + info[i]) ); //将该数相应位置置1
}
}
for( j = 0; j < n; j ++ ){ //判断哪个数被删除了
if( (1 << info[j]) & b ){
a = a ^ (1 << j); //将该数删除置为0
}
}
if( !solve(a,b) )
res[k ++] = info[i];
}
printf("Test Case #%d\n",case_ ++);
if( k ) {
printf("The winning moves are:");
for( i = 0; i < k; i ++ ){
printf(" %d",res[i]);
}
puts("");
}
else puts("There's no winning move.");
puts("");
}
return 0;
}

附上几个参考链接  http://happylch21.blog.163.com/blog/static/165639759201162243858792/
http://blog.csdn.net/zhengweihit/article/details/5799427

ACM学习-POJ-1143-Number Game的更多相关文章

  1. ACM学习-POJ-1125-Stockbroker Grapevine

    菜鸟学习ACM,纪录自己成长过程中的点滴. 学习的路上,与君共勉. ACM学习-POJ-1125-Stockbroker Grapevine Stockbroker Grapevine Time Li ...

  2. ACM学习-POJ-1003-Hangover

    菜鸟学习ACM,纪录自己成长过程中的点滴. 学习的路上,与君共勉. ACM学习-POJ-1003-Hangover Hangover Time Limit: 1000MS   Memory Limit ...

  3. ACM学习-POJ-1004-Financial Management

    菜鸟学习ACM,纪录自己成长过程中的点滴. 学习的路上,与君共勉. ACM学习-POJ-1003-Financial Management Financial Management Time Limi ...

  4. Poj 1019 Number Sequence( 数据分析和操作)

    一.题目大意 有这样一个序列包含S1,S2,S3...SK,每一个Si包括整数1到 i.求在这个序列中给定的整数n为下标的数. 例如,前80位为1121231234123451234561234567 ...

  5. acm学习指引

    acm学习心得及书籍推荐   一般要做到50行以内的程序不用调试.100行以内的二分钟内调试成功.acm主要是考算法的,主要时间是花在思考算法上,不是花在写程序与debug上. 下面给个计划练练: 第 ...

  6. ACM学习历程—POJ 3764 The xor-longest Path(xor && 字典树 && 贪心)

    题目链接:http://poj.org/problem?id=3764 题目大意是在树上求一条路径,使得xor和最大. 由于是在树上,所以两个结点之间应有唯一路径. 而xor(u, v) = xor( ...

  7. ACM学习历程——HDU 5014 Number Sequence (贪心)(2014西安网赛)

    Description There is a special number sequence which has n+1 integers. For each number in sequence, ...

  8. ACM学习历程——POJ 1700 Crossing River(贪心)

    Description A group of N people wishes to go across a river with only one boat, which can at most ca ...

  9. ACM学习历程——POJ 2376 Cleaning Shifts(贪心)

    Description Farmer John is assigning some of his N (1 <= N <= 25,000) cows to do some cleaning ...

随机推荐

  1. (转)linux下fork的运行机制

    转载http://www.cnblogs.com/leoo2sk/archive/2009/12/11/talk-about-fork-in-linux.html 给出如下C程序,在linux下使用g ...

  2. codevs 1001 舒适的路线 (并查集)

    题目描述 Description Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光. Z小镇附近共有 N(<N≤)个景点(编号为1,,,…,N),这些景点被M(<M≤)条道路连 ...

  3. qt model/view 架构基础介绍之QTreeWidget

    # -*- coding: utf-8 -*- # python:2.x #说明:QTreeWidget用于展示树型结构,也就是层次结构同前面说的 QListWidget 类似,这个类需要同另外一个辅 ...

  4. 开机后将sim/uim卡上的联系人写入数据库

    tyle="margin:20px 0px 0px; font-size:14px; line-height:26px; font-family:Arial; color:rgb(51,51 ...

  5. [转]Laravel 4之验证

    Laravel 4之验证 http://dingjiannan.com/2013/laravel-validation/ 基本验证 使用Validator::make($data, $rules)验证 ...

  6. Linux入门基础 #8:Linux拓展权限

    本文出自   http://blog.csdn.net/shuangde800 ------------------------------------------------------------ ...

  7. vue-router 快速入门

    ue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用.vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来.传统的页面应用 ...

  8. 面试前的准备---C#知识点回顾----03

    经过一天的奔波,喜忧参半,不细表 再回看下标题,C#知识点回顾 再看下内容,数据库3NF 原谅我这个标题党 今天继续回忆 1.HTTP中Post和Get区别 这忒简单了吧,大家是不是感觉到兴奋了,长舒 ...

  9. NFinal 视图—用户控件

    自定义控件 定义控件 以Label控件为例: 1.首先在Common文件夹下添加Label.cs文件,其中代码如下: //a.control的实体类必须继承NFinal.UserControl类 pu ...

  10. Centos6架设GIT服务,windows客户端使用TortoiseGit加载KEYGEN连接GIT服务器

    前几天得空,想起前一阵学了GIT还没好好实践,就在虚拟机中安装测试了一下,并简单记录了CENTOS6中GIT安装,ssh-keygen生成,客户端使用TortoiseGit加载KEYGEN连接GIT服 ...