必做题Week11

【模板】Nim 游戏

题目描述

甲,乙两个人玩 nim 取石子游戏。

nim 游戏的规则是这样的:地上有 n n n 堆石子(每堆石子数量小于 1 0 4 10^4 104),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这 n n n 堆石子的数量,他想知道是否存在先手必胜的策略。

输入格式

本题有多组测试数据。

第一行一个整数 T T T T ≤ 10 T\le10 T10),表示有 T T T 组数据

接下来每两行是一组数据,第一行一个整数 n n n,表示有 n n n 堆石子, n ≤ 1 0 4 n\le10^4 n104

第二行有 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。然后,将一枚硬币放在环上的一个节点上。两个玩家就是以这个放硬币的节点为起点开始这个游戏,两人轮流取数,取数的规则如下:

  1. 选择硬币左边或者右边的一条边,并且边上的数非 0 0 0
  2. 将这条边上的数减至任意一个非负整数(至少要有所减小);
  3. 将硬币移至边的另一端。

如果轮到一个玩家走,这时硬币左右两边的边上的数值都是 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) (N20),表示环上的节点数。

第二行 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 k1 中。

写一个程序依次读入每个杂务的工作说明。计算出所有杂务都被完成的最短时间。当然互相没有关系的杂务可以同时工作,并且,你可以假定 John 的农场有足够多的工人来同时完成任意多项任务。

输入格式

第1行:一个整数 n   ( 3 ≤ n ≤ 10 , 000 ) n\ (3 \le n \le 10{,}000) n (3n10,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 (1len100)
  • 一些必须完成的准备工作,总数不超过 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 1n,m20 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;
}
  • 25
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值