
翻黄历:Adam和Eve玩游戏,在1900年的1月1号到2001年的11月4号之间随机选一个日期,两人轮流增加日期,Adam先手。规定只能往此日期的下一天移动或者下个月的这一天移动(如果下个月没有这一天,则不能移动)。最终谁先移动到2001年的11月4号,谁就获胜。现给定日期,判断Adam是否有取胜策略。
4.2找出游戏的必胜策略
推理与动态规划算法
如果抽到最后一天肯定是必败的,那就用这个必败态往前面递推。状态按照日期先后排列,每个current状态取决于两个next状态。只要两个next状态中有一个为必败态,则Adam可以选择移动到该next状态,让Eve落败;否则Adam没有选择,只能移动到成功态,将胜利的果实拱手让给Eve。
那么只需将这102年的日期都算出来,每个日期的状态都预先算好,就可以应对题目的多case了。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX_D = 365 * 105;
const int ds[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
bool is_leap_year(int year)
{
return year % 400 == 0 || (year % 100 != 0 && year % 4 == 0);
}
struct date
{
int y, m, d;
date() { }
date(int y, int m, int d) : y(y), m(m), d(d) { }
bool operator<(const date &other) const
{
return y != other.y ? y < other.y : m != other.m ? m < other.m : d < other.d;
}
};
int D;
date days[MAX_D];
bool win[MAX_D];
void init_calendar()
{
for (int y = 1900; y <= 2001; y++)
{
for (int m = 0; m < 12; m++)
{
for (int d = 0, ed = ds[m] + ((m == 1 && is_leap_year(y)) ? 1 : 0); d < ed; d++)
{
days[D++] = date(y, m, d);
}
}
}
}
void solve()
{
memset(win, true, sizeof(win));
int start = lower_bound(days, days + D, date(2001, 10, 3)) - days;
win[start] = false;
for (int i = start - 1; i >= 0; --i)
{
win[i] = false;
if (!win[i + 1]) // 如果两个next状态中有一个必败状态
{
win[i] = true; // 则current状态为必胜状态
continue;
}
else
{
date nxt = days[i];
nxt.m++;
if (nxt.m == 12)
{
nxt.y++;
nxt.m = 0;
}
if (binary_search(days, days + D, nxt) &&
!win[lower_bound(days, days + D, nxt) - days])// 如果两个next状态中有一个必败状态
{
win[i] = true; // 则current状态为必胜状态
}
}
}
}
///////////////////////////SubMain//////////////////////////////////
int main(int argc, char *argv[])
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
init_calendar();
solve();
int T;
scanf("%d", &T);
while (T--)
{
int y, m, d;
scanf("%d%d%d", &y, &m, &d);
m--;
d--;
puts(win[lower_bound(days, days + D, date(y, m, d)) - days] ? "YES" : "NO");
}
#ifndef ONLINE_JUDGE
fclose(stdin);
#endif
return 0;
}
///////////////////////////End Sub//////////////////////////////////
| 14752920 | hankcs | 1082 | Accepted | 640K | 0MS | C++ | 2102B | 2015-09-23 21:46:40 |
另外,看到讨论区有人给出了一个简洁的规律“月与号和为偶数的天状态为必胜,为奇数的天状态为必败。特殊情况为09.30和11.30,这两天的状态也为必胜”。花点时间也可以证明,不过这些机械工作还是交给计算机比较好。
Reference
http://d.hatena.ne.jp/komiyam/20120331/1333125295
http://blog.csdn.net/crescent__moon/article/details/18558109
知识共享署名-非商业性使用-相同方式共享:码农场 » POJ 1082 Calendar Game 题解《挑战程序设计竞赛》
码农场