http://poj.org/problem?id=2484
给出N个硬币围成一个圈,然后两个人从这圈硬币中轮流拿1个或毗邻的2个硬币。直到全部拿完为止,最后一个拿的人为,胜者。
当n==1 || n==2时,明显先手必胜。
当n==3时,明显先手必败。
由于每次只可取1或2个,而取2个时,2个必须相邻,推断有:
当n>3时,若n为偶数,先手无论如何取,后手可在先手对称的位置上取同等数量,于是先手必败。
若n为奇数,先手取1个时,后手可在先手对称的位置上取2个,之后无论先手如何取,后手都可在先手对称的位置上取同等数量,先手必败。
如果先手一开始取2个时,后手可在先手对称的位置上取1个,之后还剩下偶数个,可如上推出先手必败。故当 n>3时,先手必败。
Source Code
Problem: 2484 User: liangrx06
Memory: 236K Time: 0MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int main(void)
{
int n;
while (cin >> n && n) {
if (n <= 2) printf("Alice\n");
else printf("Bob\n");
}
return 0;
}
http://poj.org/problem?id=2348
给两堆石子(题目中是数,配合一下上文这里说石子),两人依次取石子,规则是:每次从石子数较多的那堆取(两堆石子数目相等时任选一堆),取的数目只能为石子少的那一堆的正整数倍。
最后取完一堆石子者胜。问给定情况下先手胜负情况。
书中有详细的讲解,假设a>b,若a%b == 0或a-b > b则必胜,否则只能取a-b,然后看后续情况。
但这个题困扰我的问题是循环条件。这个能AC代码的循环判断条件是:
while (a%b && a-b < b)
而我最开始WA的代码中的判断条件是:
while (a-b < b)
判断条件之前已经对a和b的值进行过处理使a>=b,而且题目中说明a和b都是正数。那么这两个判断条件难道效果不是一样的吗?另外这样写也不会有溢出危险,除非负数。
已经测试过数据中不含0,因为while (!b && a-b < b)也不WA。难道测试数据中有负数?(但这不符合题目说明)
总之我认为这个题要么数据有问题,要么我脑子秀逗了。
另外有博文说:最后的结论是如果两个数相等,或者两数之比大于斐
波拉契数列相邻两项之比的极限((sqrt(5)+1)/2),则先手胜,否则后手胜。
这个比较牛,但不知如何推出来的?
Source Code
Problem: 2348 User: liangrx06
Memory: 236K Time: 16MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int main(void)
{
int a, b;
while (cin >> a >> b, a || b) {
if (a < b) swap(a, b);
bool ans = true;
while (a%b && a-b < b) {
b = a-b;
a = a-b;
ans = !ans;
}
if (ans) printf("Stan wins\n");
else printf("Ollie wins\n");
}
return 0;
}
http://poj.org/problem?id=1082
从1900-1-1到2001-11-4期间任一个日期开始,每次要么向后移动一天,要么向后移动一个月。最后准确到达2001-11-4的为胜者。
从后往前推,对于某一天的胜败由两天决定:后一天和后一个月的同一天(当然有的没有第二个决定因素)
若两者都是必胜态,则这一天必败。若有一天为必败,则这一天必胜。
2001年11月4日到2001年10月4日胜败是交替的,设月+日等于M。发现M为偶数时必胜,为奇数是必败。
2001年10月4日必胜,10月3日由10月4日和11月3号决定,两者都是必胜,所以10月3号必败。同理可判定10月剩余的日子。相邻的两个月的同一天胜败情况相反,但这一天M的奇偶性也相反(月份或日期两者只有一个减一),所以结论成立。
下面考虑特殊情况:
9月只有30天,9月30号由10月1号(必败)和10月30号(必胜)决定,为必胜。与上述结论矛盾,但9月29号为必胜,9月后面的日子依然遵循这样的规律,可见9月30号为一个特殊情况,同样的特殊情况还有11月30号(用同样的方法判断4,6月30号发现遵循结论),对于2月(不管是闰年还是平年)发现也符合。
所以可的结论:除9月30号和11月30号外,M为偶数必胜,为奇数必败。
另外也可以写程序动态规划求解,代码相对要麻烦一些,但思路清晰。这位仁兄就是这样做的:POJ1082动态规划求解
Source Code
Problem: 1082 User: liangrx06
Memory: 240K Time: 0MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int main(void)
{
int t, y, m, d;
bool ans;
cin >> t;
while (t--) {
scanf("%d%d%d", &y, &m, &d);
ans = true;
if ((m+d)%2) ans = false;
if(d == 30 && (m == 9 || m == 11))
ans = true;
if (ans) printf("YES\n");
else printf("NO\n");
}
return 0;
}
http://poj.org/problem?id=2068
有2*n个人坐在一起,分为2组(交叉坐即:1、3、5….是我方,2、4、6….是对方),共有s个石头,每次挨着取石头,石头不能超过当前人取最大的限制,若谁取到最后一个石头,那么就算失败。现在问我方是否能胜利,胜利输出1,失败输出0。
动态规划求解,dp[i][j]表示为第i个人剩下j个石头的胜负情况。初始化所有值为0,然后类似于素数筛法的做法由必败态筛出必胜态。注意思路要清晰,顺序不能搞反。
Source Code
Problem: 2068 User: liangrx06
Memory: 324K Time: 32MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 10;
const int S = 1<<13;
int n, s;
int m[2*N];
bool dp[2*N][S+1];
int main(void)
{
while (scanf("%d", &n) != EOF && n) {
scanf("%d", &s);
for (int i = 0; i < 2*n; i ++)
scanf("%d", &m[i]);
memset(dp, 0, sizeof(dp));
for (int j = 1; j < s; j ++) {
for (int i = 0; i < 2*n; i ++) {
int ii = (i+2*n-1)%(2*n);
if (dp[i][j] == false) {
for (int k = 1; k <= m[ii]; k ++) {
if (j + k > s) break;
dp[ii][j+k] = true;
}
}
}
}
int ans = dp[0][s] ? 1 : 0;
printf("%d\n", ans);
}
return 0;
}
http://poj.org/problem?id=3688
这里写代码片
http://poj.org/problem?id=1740
取石子游戏,有n堆石子(n<=10),每堆有stone[i]颗石子(<=100),A和B玩游戏,每次操作的人可以任选一堆石子拿出来起码一颗,然后可以选择把剩下的石子移到其他的非空石子堆中,给定局面问是否先手必胜。
传说中楼教主的“男人八题”之一!
这题主要是寻找必败态。
如果只有一堆石子,先手必胜。
如果有两堆石子,并且两堆石子的数量相等,那么先手采取什么样的策略,后手采取一样的策略,先手必败。
如果有三堆石子,那么先手可以在第一步取到只剩两堆相同数量的石子,先手必胜。
如果有四堆石子,由于三堆石子是必胜态,所以无论是先手还是后手都想逼对方取完某一堆石子,只有在四堆石子都为1时,擦能迫使某一方取完一堆石子,通过分析可知,只有当四堆石子可以分成两两相等的两队时,先手必败。
由上我们可以猜测,n 堆石子,当且仅当n为偶数,且n对石子可以分成n/2对两两相等的石子时,先手必败。
证明:
第一条性质:终点是必败态,满足。
第二条性质:必胜点一定有种策略可以进入必败态。
由以上可知,必胜点是当n为奇数或者n为偶数且不能分成n/2对两两相等的石子。
当n为奇数时我们一定可以把其中一堆分到其他堆中,使成n/2对两两相等的石子,第二种情况也是成立的。
第三条性质:必败点只能到达必胜点。显然成立。
Source Code
Problem: 1740 User: liangrx06
Memory: 248K Time: 0MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 10;
int main(void)
{
int n;
int a[N];
while (cin >> n && n) {
for (int i = 0; i < n; i ++)
cin >> a[i];
int ans = 1;
if ((n&1) == 0) {
sort(a, a+n);
bool flag = true;
for (int i = 0; i < n; i += 2)
if (a[i] != a[i+1]) flag = false;
if (flag) ans = 0;
}
cout << ans << endl;
}
return 0;
}