SDNU-ACM第一次月赛&&地大武汉选拔赛

前言

不开ll见祖宗!我愿称之为因为ll打烂了的月赛。

D是签到题这里不再写了。

Problem A(未A)

 思路

数学题,非常妙,赛场上只想出来了一半。

一开始火速搓了一个二分思想wa掉了,做完别的再回来看就想到了m的作用:由于电子秤且知道真假的质量,那么第一堆放一个,第二堆放两个,直到第m堆放m个,我们就可以知道称上是否有、有几个假,由此,每次可以测出m+1堆

剩下是我没想到的:如果n>m+1了,我们可以把m+1堆捆成一坨,造m+1坨,若确定了一坨,则在本坨里量一次即可,依次类推,我们可知答案为log(m+1) n。

Problem B

简单写个函数,没什么好说的。

AC代码

#include <bits/stdc++.h>
using namespace std;

int f(int a, int b, int c, int d, int x) {
	int ans = a * x * x * x + b * x * x + c * x + d;
	return ans;
}

int main() {
	int n;
	scanf("%d", &n);
	int a, b, c, d, q, w, e, r, x1, x2;
	while (n--) {
		cin >> a >> b >> c >> d >> q >> w >> e >> r >> x1 >> x2;
		cout << max(f(a, b, c, d, x1), f(q, w, e, r, x2)) << '\n';
	}
	return 0;
}

Problem E

思路

不懂的话就模拟一下八进制或者模拟一遍样例,苯人就是爱模拟。

在数字b中,每位数字都是num取模序列a中相应位,然后把整除的数给到下一位作为进位,和十进制是一样的模式,只是数变了可能就会有点晕。

另:前几天看了大数加法、乘法、阶乘和,都是一个理念,思路非常像,想通一个就都通了。

回顾

做这题的时候排名好像已经不太好看了,所以有点急,第一次wa了之后开始检查,发现了一个没开ll的量,但我觉得不是因为这里错了,就又改了两遍输出格式,废了一个小数+2wa,这个时候排名可以说是非常难看了,真的很难绷,然后直接开摆改回去加了ll,没想到他过了,绿a出现的那一刻彻底绷不住了,真想给自己俩大比兜,坐赛场上缓了得有十分钟才继续做题。

真的因为ll摔了大跟头,以后不可以再因为ll错。

AC代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long

int main() {
	ll x;
	int n;
	int arr[10000], b[10000] = {0};
	cin >> n >> x;
	ll t = x;
	for (int j = 0; j < n; ++j) {    //看提目要求,前面数都是0了也要输出
		cin >> arr[j];
		b[j] = t % arr[j];    //每一位都是基数的模
		t /= arr[j];          //进位
	}
	cout << b[0];
	for (int j = 1; j < n; ++j) {
		cout << " " << b[j];
	}
	cout << '\n';
	return 0;
}

Problem J

思路

这场比赛感谢这个博弈捞了我一下,E题罚时的影响太大了。对这题我的评价是,奇偶性真的可以玩的很花。

赛时思路:样例很重要,样例给我了一点灵感。因为我个人比较抵触跟矩阵沾边的东西,所以其实一开始我的思路是把矩形都变成一行长条(样例让我灵机一动),那么2、3步一轮如何获胜,这样就很容易想到奇偶性,然后再找问题,条和矩形的区别是什么。我的思维会优先广搜,找到最简的可能思路再深想,所以其实我的思路会有点跳跃,我尽量整理成顺一点的(但是在赛场上直接想到顺的思路其实很难,这要求你把每一个点都理解的很透并且看到就想到,所以我更倾向于多看方法,跳跃思维,避免一条路走到黑发现是南墙。但是这样也有弊端,就是一些正解就是麻烦的题我会去寻找别的做法,试试这试试那,浪费时间且不一定能搞出来)。

复盘:(我目前遇到的)博弈的特点就是放弃枚举的思路,一旦开始想枚举就完了,越想越乱,从找规律的思维入手,氪金每次可走1或2步,我每次只能走1步,那我们玩一局游戏走的步数可能是2或3, 因为我们都绝对聪明且无法掌控对方走向,所以要找的就是只有一种结局的情况。

我先手,如果边长和是偶数,那么氪金只需要每次走一步,无论怎么走我必败

如果边长和是奇数,若氪金还是每次走一步,那氪金必败,他该如何破解这个情况呢,显然就是在某一轮中走两步,让剩下的边长和回归偶数情况则可保证必胜。于是我唯一胜利的可能就是不让氪金有走两步的机会,氪金要想走两步只能是斜对角走,什么情况他不能再斜对角走了呢:有一边已经到墙了。思路到这里已经差不多成型了:边长和是奇数的时候,当本来就无法走斜线时我必赢;当有两列或两行,由于我先手,在第一次走的时候走一列/一行让他无法继续走斜线时我必赢。其他情况则他必赢。

AC代码

#include <bits/stdc++.h>
using namespace std;

int main() {
	int t;
	cin >> t;
	while (t--) {
		int m, n;
		cin >> m >> n;
		bool flag = 0;
		if ((m + n) % 2 == 0) {
			flag = 0;
		} else {
			if (m == 0 || n == 0 || m == 1 || n == 1) {
				flag = 1;
			}
		}
		if (flag == 1)
			cout << "win\n";
		else
			cout << "loss\n";
	}
	return 0;
}

Problem M(未A)

思路

dfs&&bfs,补题A了,但是因为交错了题wa了一下午,笑死,这就是笨比。

dfs注意点:是否需要回溯。

油田和马踏飞燕其实是两种题型,油田只需要找出没被找过的@就可以,只需标记不用回溯,其实相当于只查了一遍全图。而马踏飞燕是求组合,需要遍历很多遍同一个部分,所以在每一part的遍历前标记,遍历后回溯。

#include <bits/stdc++.h>    //dfs
using namespace std;
#define ll long long
ll K = 998244353;
ll C = 499122177;        //先算出2的逆元,省去了快速幂
ll n, m;
ll cnt = 13;

void dfs(ll cur, ll step) {
	if (step > 12) {
		return;
	}
	if (cur == m) {
		cnt = min(cnt, step);        //类比1015二叉树
		return;
	}
	for (int i = 1; i <= 3; ++i) {
		ll b;
		switch (i) {
			case 1:
				b = cur * 3 % K;
				break;
			case 2:
				b = cur * C % K;
				break;
			case 3:
				b = (cur - 123456) % K;
				break;
		}
		dfs(b, step + 1);          //这里不用标记,与全排列不同,而更像马踏飞燕
	}                              //因为每一步都可以走一样的,同一个数的step不同也会影响答案
}

int main() {
	int T;
	cin >> T;
	while (T--) {
		cnt = 13;                  //初始化
		cin >> n >> m;
		dfs(n, 0);
		if (cnt == 13)
			cout << "-1\n";
		else
			cout << cnt << '\n';
	}
	return 0;
}

 bfs注意点:初始化初始化初始化,清空队列清空队列清空队列。标记注意点见下。

#include <bits/stdc++.h>            //bfs
using namespace std;
#define ll long long
const ll K = 998244353;
const ll C = 499122177;
ll m;
struct stu {
	ll num;
	int step;
};
queue<stu>q;
map<ll, bool>vis;                 //标记

void bfs() {
	while (!q.empty()) {
		stu cur = q.front();
		q.pop();
		//vis[cur.num] = 1;出队时标记没什么大用,见下。
		if (cur.num == m) {
			cout << cur.step << '\n';
			return;
		}
		for (int i = 1; i <= 3; ++i) {
			stu b;
			b.step = cur.step + 1;
			switch (i) {
				case 1:
					b.num = cur.num * 3 % K;
					break;
				case 2:
					b.num = cur.num * C % K;
					break;
				case 3:
					b.num = (cur.num - 123456 + K) % K;
					break;
			}
			if (b.step <= 12 && vis[b.num] != 1)
				q.push(b);
			vis[b.num] = 1;  //要在入队的时候就标记,或者选到他了就标记。
            //与dfs不同,bfs由于广度,能保证不走回头路,cur状态的step永远是已判断序列中最大的
            //不需考虑dfs上述step不同结果不同的情况。
            //若仅在出队时判断,那在其入队后出队前,可能会被重复入队,无法达到判断目的。
		}
	}
	cout << "-1\n";
	return;
}

int main() {
	int t;
	cin >> t;
	while (t--) {
		vis.clear();                //清空标记
		while (!q.empty()) {        //清空队列
			q.pop();
		}
		stu a;
		a.step = 0;
		cin >> a.num >> m;
		q.push(a);
		bfs();
	}
	return 0;
}

总结

绷住心态,记得开ll,人不能总在同一个地方摔跟头,记住。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值