1. 程式人生 > 實用技巧 >「NOIP模擬賽」日曆遊戲

「NOIP模擬賽」日曆遊戲

「NOIP模擬賽」日曆遊戲

「問題描述」

moreD和moreD的寵物CD正在玩一個日曆遊戲,開始時,他們從1900年1月1日到2012年12月22日(你懂的……)選一個日期開始,依次按照如下規則之一向後跳日期:

  1. 跳到日曆上的下一天。
  2. 跳到日曆上的下個月的同一天(如果不存在,則不能這麼做)。

要是誰正好到達2012年12月22日那麼他就贏了,如果到達這天之後的日期那他就輸了——原因你也懂的。

每次都是moreD先走的。

現在,給你一個日期,請問moreD一定能贏嗎?

「輸入」

輸入共T行,每行三個整數,Y、M、D,分別表示年、月、日。日期在1900年1月1日到2012年12月22日之間(包含兩端)。

T並不在輸入資料當中。

「輸出」

要是moreD一定能贏,輸出一行YES,否則輸出NO。

「輸入輸出樣例一」

2012 12 20
NO

「輸入輸出樣例二」

2012 12 21
YES

「資料描述」

對於50%的資料,是1949年1月1日後的日期。 T <= 5

對於100%的資料,是1900年1月1日後的日期。T <= 10


Solution

%你賽\(T1\)遇到這種毒瘤題。。也沒學過博弈論,於是自己玩了玩,整到了80pts,還以為是正確性有問題,沒想到是一個小細節的鍋。

接下來講講我的思路。

首先,如果這兩個人都天資聰慧,兩種操作都會用,我這種蒟蒻不會判斷啊。。

所以我們假設他們都只會往後跳一天,也就是禁用操作二。

很顯然,在這樣的限制下,用結束日期減去給定日期,如果是奇數天,那麼我方必勝,否則必敗。

那麼操作二在什麼情況下會改變他們是傻子時候的結果呢?

我們發現,拋開時間,就看剩餘天數\(d\)的奇偶性。如果是操作一,那麼每次操作都會改變\(d\)的奇偶性。所以改變\(d\)奇偶性的操作二啥用沒有,不考慮。

那麼什麼情況下的操作二不會改變\(d\)的奇偶性呢?跳偶數天

我們發現,跳到下一月,實際跳了當前月的天數。(在跳月可行的情況下)

就拿當前日期為例。我們要從\(8.2\)跳到\(9.2\),實際是跳了\(8\)月天數\(31\)天而不是\(9\)月天數\(30\)天。

所以跳月能對答案造成影響的月份有:\(4,6,9,11\)

月以及不閏的\(2\)月。

於是模擬,從給定日期往後跳,如果能跳的,就把計數器加一。

然後就是來討論這兩個數值如何推出最終答案。


記剩餘天數為\(k\),跳月能造成影響的次數為\(p\)

根據\(k\),我們有一個初始的狀態表示是否必勝。(其實就是\(k\)為奇數必勝,\(k\)為偶數必敗)

然後來看\(p\)的影響。

a.如果我方本來就是必勝

1.\(p\)是奇數。必敗。

我方必勝,對方必敗,有改變戰局的機會,對方肯定要使用。使用之後變為我方必敗,對方必勝的情況。這個時候還有偶數次改變戰局的機會。我方用一次,對方就對應的用一次。最終會導致我方保持必敗,對方保持必勝。

2.\(p\)是偶數。必勝。

我方必勝,對方必敗,有改變戰局的機會,對方肯定要使用。而此時對方每使用一次,我就對應的使用一次。最後會導致我方保持必勝,對方保持必敗。

b.如果我方本來就是必敗

1.\(p\)是奇數。必勝。

同a1的情況,只是我們到了對方的位置。

2.\(p\)是偶數。必敗。

同a2的情況,只是我們到了對方的位置。


於是我們發現答案只和\(k,p\)的奇偶性有關。

但是這裡還有一個小小的特殊情況:

\(p==0\)時,直接判斷\(k\)的奇偶性。\(k \mod 2=1\)必勝,否則必敗。

否則,判斷\((k+p)\)的奇偶性。\(((k+p) \mod 2=0)\)必勝,否則必敗。

這道題就水過去了。

程式碼中注意模擬的時候讓月份始終保持在\([1,12]\)中,不然像我這樣少一個細節就\(20pts\)拜拜了。


\(CODE\)

#include<bits/stdc++.h>
using namespace std;
int mon[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int chg[13]={0,1 ,0 ,1 ,0 ,1 ,0 ,1 ,1 ,0 ,1 ,0 ,1 };
int rn(int y)
{
	if(y%100==0&&y!=2000)return 0;
	if(y%4==0)return 1;
	return 0;
}
int last_days(int y,int m,int d)
{
	int sum=0;
	mon[2]=28+rn(y);
	if(d>22)sum+=(mon[m]-d+22),m++;
	else sum+=(22-d);
	//先把日固定在22,一定能跳月 
	if(m==13)m=1,y++;
	d=22;
	while(y!=2012||m!=12||d!=22)
	{
		mon[2]=28+rn(y);
		sum+=mon[m];
		m++;
		if(m==13)m=1,y++;
	}
	return sum;
}
int change_times(int y,int m,int d)
{
	int t=0;
	while(y!=2012||m!=12)
	{
		chg[2]=rn(y);
		mon[2]=28+rn(y);
		int nxt=m+1;
		if(nxt>12)nxt=1;
		if(d<=mon[nxt])
		{
			if(!chg[m])t++;
			//如果此步不對答案產生貢獻,那就是一次轉機 
		}
		else d=1;
		m++;
		if(m>12)m=1,y++;
	}
	return t;
}
int main()
{
	int y,m,d;
	while(~scanf("%d%d%d",&y,&m,&d))
	{
		int k=last_days(y,m,d);
		//如果不跳月是否必勝 
		int p=change_times(y,m,d);
		//是否能靠此扭轉戰局 
		if(!p)puts(k%2?"YES":"NO");
		else puts((k+p)%2==0?"YES":"NO");
	}
	return 0;
}

一道類似的題目:洛谷\(P1512\)

感謝閱讀。