【模板】Nim 游戏
题目描述
甲,乙两个人玩 nim 取石子游戏。
nim 游戏的规则是这样的:地上有 n n n 堆石子(每堆石子数量小于 1 0 4 10^4 104),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这 n n n 堆石子的数量,他想知道是否存在先手必胜的策略。
输入格式
本题有多组测试数据。
第一行一个整数 T T T ( T ≤ 10 T\le10 T≤10),表示有 T T T 组数据
接下来每两行是一组数据,第一行一个整数 n n n,表示有 n n n 堆石子, n ≤ 1 0 4 n\le10^4 n≤104。
第二行有 n n n 个数,表示每一堆石子的数量.
输出格式
共
T
T
T 行,每行表示如果对于这组数据存在先手必胜策略则输出 Yes
,否则输出 No
。
样例 #1
样例输入 #1
2
2
1 1
2
1 0
样例输出 #1
No
Yes
思路:模板
代码
#include<bits/stdc++.h>
using namespace std;
int t, n, a[10010];
int main()
{
cin >> t;
while (t--)
{
int sum = 0;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
sum ^= a[i];
}
if (sum == 0)puts("No");
else puts("Yes");
}
return 0;
}
取数游戏 II
题目描述
有一个取数的游戏。初始时,给出一个环,环上的每条边上都有一个非负整数。这些整数中至少有一个 0 0 0。然后,将一枚硬币放在环上的一个节点上。两个玩家就是以这个放硬币的节点为起点开始这个游戏,两人轮流取数,取数的规则如下:
- 选择硬币左边或者右边的一条边,并且边上的数非 0 0 0;
- 将这条边上的数减至任意一个非负整数(至少要有所减小);
- 将硬币移至边的另一端。
如果轮到一个玩家走,这时硬币左右两边的边上的数值都是 0 0 0,那么这个玩家就输了。
如下图,描述的是 Alice 和 Bob 两人的对弈过程(其中黑色节点表示硬币所在节点)。
各图的结果为:
- A \text{A} A:轮到 Alice 操作;
- B \text{B} B:轮到 Bob 操作;
- C \text{C} C:轮到 Alice 操作;
- D \text{D} D:轮到 Bob 操作。
D \text{D} D 中,轮到 Bob 走时,硬币两边的边上都是 0 0 0,所以 Alice 获胜。
现在,你的任务就是根据给出的环、边上的数值以及起点(硬币所在位置),判断先走方是否有必胜的策略。
输入格式
第一行一个整数 N N N ( N ≤ 20 ) (N \leq 20) (N≤20),表示环上的节点数。
第二行 N N N 个数,数值不超过 30 30 30,依次表示 N N N 条边上的数值。硬币的起始位置在第一条边与最后一条边之间的节点上。
输出格式
仅一行。若存在必胜策略,则输出 YES
,否则输出 NO
。
样例 #1
样例输入 #1
4
2 5 3 0
样例输出 #1
YES
样例 #2
样例输入 #2
3
0 0 0
样例输出 #2
NO
思路:找规律发现,如果两个人都是绝顶聪明的话,两者都会不断把对方逼上绝境,故可计算起点从两边到第一个0的距离,如果存在奇数,那么先手就能赢
代码
#include<bits/stdc++.h>
using namespace std;
int n, s[1001];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
}
int a = 0; while (s[++a]);
int b = 0; while (s[n + 1 - (++b)]);
if (--a % 2 == 1 || --b % 2 == 1)cout << "YES";
else cout << "NO";
return 0;
}
杂务
题目描述
John 的农场在给奶牛挤奶前有很多杂务要完成,每一项杂务都需要一定的时间来完成它。比如:他们要将奶牛集合起来,将他们赶进牛棚,为奶牛清洗乳房以及一些其它工作。尽早将所有杂务完成是必要的,因为这样才有更多时间挤出更多的牛奶。
当然,有些杂务必须在另一些杂务完成的情况下才能进行。比如:只有将奶牛赶进牛棚才能开始为它清洗乳房,还有在未给奶牛清洗乳房之前不能挤奶。我们把这些工作称为完成本项工作的准备工作。至少有一项杂务不要求有准备工作,这个可以最早着手完成的工作,标记为杂务 1 1 1。
John 有需要完成的 n n n 个杂务的清单,并且这份清单是有一定顺序的,杂务 k ( k > 1 ) k\ (k>1) k (k>1) 的准备工作只可能在杂务 1 1 1 至 k − 1 k-1 k−1 中。
写一个程序依次读入每个杂务的工作说明。计算出所有杂务都被完成的最短时间。当然互相没有关系的杂务可以同时工作,并且,你可以假定 John 的农场有足够多的工人来同时完成任意多项任务。
输入格式
第1行:一个整数 n ( 3 ≤ n ≤ 10 , 000 ) n\ (3 \le n \le 10{,}000) n (3≤n≤10,000),必须完成的杂务的数目;
第 2 2 2 至 n + 1 n+1 n+1 行,每行有一些用空格隔开的整数,分别表示:
- 工作序号(保证在输入文件中是从 1 1 1 到 n n n 有序递增的);
- 完成工作所需要的时间 l e n ( 1 ≤ l e n ≤ 100 ) len\ (1 \le len \le 100) len (1≤len≤100);
- 一些必须完成的准备工作,总数不超过 100 100 100 个,由一个数字 0 0 0 结束。有些杂务没有需要准备的工作只描述一个单独的 0 0 0。
保证整个输入文件中不会出现多余的空格。
输出格式
一个整数,表示完成所有杂务所需的最短时间。
样例 #1
样例输入 #1
7
1 5 0
2 2 1 0
3 3 2 0
4 6 1 0
5 1 2 4 0
6 8 2 4 0
7 4 3 5 6 0
样例输出 #1
23
思路:把每个编号的实例都遍历一遍,把每次最大的时间更新,因为第k个事件的前置事件的编号不会第k个事件的后面,所以按顺序遍历就能把所有事件给遍历掉,然后只需要动态规划找最长的时间即可
代码
#include<bits/stdc++.h>
using namespace std;
int n, f[10010], ans;
int main()
{
int id, len, pre, addition;
cin >> n;
for (int i = 1; i <= n; i++)
{
addition = 0;
cin >> id >> len >> pre;
while (pre != 0)
{
addition = max(addition, f[pre]);//找最长的前置时间
cin >> pre;
}
f[i] = len + addition;
ans = max(ans, f[i]);//更新答案,只要最长就会是答案
}
cout << ans << endl;
return 0;
}
[NOIP2002 普及组] 过河卒
题目描述
棋盘上 A A A 点有一个过河卒,需要走到目标 B B B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C C C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示, A A A 点 ( 0 , 0 ) (0, 0) (0,0)、 B B B 点 ( n , m ) (n, m) (n,m),同样马的位置坐标是需要给出的。
现在要求你计算出卒从 A A A 点能够到达 B B B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
输入格式
一行四个正整数,分别表示 B B B 点坐标和马的坐标。
输出格式
一个整数,表示所有的路径条数。
样例 #1
样例输入 #1
6 6 3 3
样例输出 #1
6
提示
对于 100 % 100 \% 100% 的数据, 1 ≤ n , m ≤ 20 1 \le n, m \le 20 1≤n,m≤20, 0 ≤ 0 \le 0≤ 马的坐标 ≤ 20 \le 20 ≤20。
【题目来源】
NOIP 2002 普及组第四题
思路:就是动态规划每个点的可能走过的数量
代码
#include<bits/stdc++.h>
using namespace std;
const int dir[8][2] = { {1,2},{1,-2},{2,1},{2,-1},{-1,2},{-1,-2},{-2,1},{-2,-1} };
bool d[30][30];
long long f[30][30], n, m, cx, cy;
int main() {
cin >> n >> m >> cx >> cy;
d[cx][cy] = true;
for (int i = 0; i < 8; i++) {
int tx = cx + dir[i][0], ty = cy + dir[i][1];
if (tx >= 0 && tx <= n && ty >= 0 && ty <= m) d[tx][ty] = true;
}
f[0][0] = 1;
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
if (d[i][j] == false) {
if (i) f[i][j] += f[i - 1][j];
if (j) f[i][j] += f[i][j - 1];
}
}
}
cout << f[n][m] << endl;
return 0;
}