(简单博弈,奇偶轮换性)Calendar Game

来源:互联网 发布:阿里云域名绑定tomcat 编辑:程序博客网 时间:2024/06/15 20:52
Calendar Game
Time Limit: 1000MS Memory Limit: 10000KTotal Submissions: 4826 Accepted: 2274

Description

Adam and Eve enter this year's ACM International Collegiate Programming Contest. Last night, they played the Calendar Game, in celebration of this contest. This game consists of the dates from January 1, 1900 to November 4, 2001, the contest day. The game starts by randomly choosing a date from this interval. Then, the players, Adam and Eve, make moves in their turn with Adam moving first: Adam, Eve, Adam, Eve, etc. There is only one rule for moves and it is simple: from a current date, a player in his/her turn can move either to the next calendar date or the same day of the next month. When the next month does not have the same day, the player moves only to the next calendar date. For example, from December 19, 1924, you can move either to December 20, 1924, the next calendar date, or January 19, 1925, the same day of the next month. From January 31 2001, however, you can move only to February 1, 2001, because February 31, 2001 is invalid. 

A player wins the game when he/she exactly reaches the date of November 4, 2001. If a player moves to a date after November 4, 2001, he/she looses the game. 

Write a program that decides whether, given an initial date, Adam, the first mover, has a winning strategy. 

For this game, you need to identify leap years, where February has 29 days. In the Gregorian calendar, leap years occur in years exactly divisible by four. So, 1993, 1994, and 1995 are not leap years, while 1992 and 1996 are leap years. Additionally, the years ending with 00 are leap years only if they are divisible by 400. So, 1700, 1800, 1900, 2100, and 2200 are not leap years, while 1600, 2000, and 2400 are leap years.

Input

The input consists of T test cases. The number of test cases (T ) is given in the first line of the input file. Each test case is written in a line and corresponds to an initial date. The three integers in a line, YYYY MM DD, represent the date of the DD-th day of MM-th month in the year of YYYY. Remember that initial dates are randomly chosen from the interval between January 1, 1900 and November 4, 2001.

Output

Print exactly one line for each test case. The line should contain the answer "YES" or "NO" to the question of whether Adam has a winning strategy against Eve. Since we have T test cases, your program should output totally T lines of "YES" or "NO".

Sample Input

3 2001 11 3 2001 11 2 2001 10 3 

Sample Output

YESNONO

Source

Taejon 2001
题目大意:给定一个日期, 玩家轮流选择给它"加月"或"加日",最先到达2001.11.04的胜利,问先手必胜还是必败?
分析:自己做了常规方法一, 后来在别人的blog发现了巧妙的方法二:
方法一: PN大法.
方法二: 奇偶轮换法.

方法一思路是常规的, 这里只对方法二作一些个人的解析:
定义:sum为日期中月和日的和.
一般地,新的sum比旧的sum多1,sum的值满足奇偶轮换性,而“目标日期”11.4的sum为奇数,自然地我们规定11.4的sum为P态;那么是否所有为偶数的sum都是N态,所有为奇数的sum都是P态呢(即所有偶数的sum都至少有一种方式更新为奇数的sum,所有奇数的sum都只能更新为偶数的sum)
这里按加月和加日的选择分两种情况:
①如果选择加月,新sum比旧sum多1,那么sum的值满足奇偶轮换性。
②如果选择加日,如果不需要向月进位,新sum比旧sum多1,那么sum的值满足奇偶轮换性;如果需要向月进位,手动枚举所有情况,发现不满足奇偶轮换性的有五种情况:闰年的2.28→3.1,4.30→5.1,6.30→7.1,  9.30→10.1,  11.30→12.1。
所以,如果当前sum是偶数且处于这些状态中的一种,可以选择加月使游戏回到奇偶循环。故所有为偶数的sum都是N态。
但是所有为奇数的sum都是P态吗?不是的,从上面五种特殊情况中可见9.30→10.1和11.30→12.1并不符合这个规律。故9.30和11.30也是N态,其它奇数是P态。

综上,如果先手一开始处于9.30或10.1则必胜,一开始处于偶数的sum也必胜(这时只要不让对手到达9.30和10.1而使sum在符合奇偶轮换性的环境下进行即可),否则必败。

方法一不解析, 代码(但是注意这个代码在POJ提交是WA, 在杭电提交是AC,估计是POJ上有些数据没有通过,没有再仔细找错......):
#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int maxn = 20020000;bool a[2002][13][32];bool is_leap(int y){if (y % 100 == 0)return y % 400 == 0;elsereturn y % 4 == 0;return false;}bool is_exist(int y, int m, int d){bool flag = false;if (m == 2){if (is_leap(y)){if (d <= 29)flag = true;}else{if (d <= 28)flag = true;}}else if (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 10){if (d <= 31)flag = true;}else{if (d <= 30)flag = true;}return flag;}bool judge(int y, int m, int d, int cho){bool flag = false;if (cho){if (m == 2){if (is_leap(y)){if (d < 29)flag = true;}else{if (d < 28)flag = true;}}else if (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 10){if (d<31)flag = true;}else{if (d<30)flag = true;}if (flag)return a[y][m][d + 1];else{if (m < 12)return a[y][m + 1][1];elsereturn a[y + 1][1][1];}}else if (!cho){if (m == 12){m = 1;y++;}else m++;if (is_exist(y, m, d)){if (y * 10000 + m * 100 + d <= 20011104)return a[y][m][d];}elsereturn true;}}void init(){a[2001][11][4] = false;int y, m, d, cho;for (y = 2001; y >= 1900; y--)for (m = 12; m >= 1; m--)for (d = 31; d >= 1; d--){if (y * 10000 + m * 100 + d >= 20011104)continue;if (is_exist(y, m, d)){a[y][m][d] = false;for (cho = 0; cho <= 1; cho++)a[y][m][d] |= (judge(y, m, d, cho)==false);}}}int main(){init();int T;cin >> T;while (T--){int y, m, d;cin >> y >> m >> d;if (a[y][m][d])cout << "YES" << endl;elsecout << "NO" << endl;}}

方法二,奇偶轮换性:
#include <iostream>using namespace std;bool win(int m,int d){if((m+d)%2==0)return true;if(d==30 && (m==9 ||m==11))return true;return false;}int main(){freopen("in.txt","r",stdin);int i,n;int y,m,d;cin>>n;for(i=0;i<n;i++){cin>>y>>m>>d;if(win(m,d))cout<<"YES"<<endl;else  cout<<"NO"<<endl;}return 0;}



0 0
原创粉丝点击