CSP-S 2023 密码锁

P9752 [CSP-S 2023] 密码锁-传送门

题面

小 Y 有一把五个拨圈的密码锁。如图所示,每个拨圈上是从 0 到 9 的数字。每个拨圈都是从 0 到 9 的循环,即 9 拨动一个位置后可以变成 0 或 8,

因为校园里比较安全,小 Y 采用的锁车方式是:从正确密码开始,随机转动密码锁仅一次;每次都是以某个幅度仅转动一个拨圈或者同时转动两个相邻的拨圈。

当小 Y 选择同时转动两个相邻拨圈时,两个拨圈转动的幅度相同,即小 Y 可以将密码锁从 0  0  1  1  5 转成 1  1  1  1  5,但不会转成 1  2  1  1  5。

时间久了,小 Y 也担心这么锁车的安全性,所以小 Y 记下了自己锁车后密码锁的 n 个状态,注意这 n 个状态都不是正确密码。

为了检验这么锁车的安全性,小 Y 有多少种可能的正确密码,使得每个正确密码都能够按照他所采用的锁车方式产生锁车后密码锁的全部 n 个状态。

输入样例

1

1 2 3 4 5

输出样例

81

数据范围

解析

这道题其实就是让你去找所有真密码,可以通过转动一个轮子或相邻两个轮子,得到所有的假密码。暴力枚举,有风险,在洛谷上有大佬写出来了,可以参考,用空间和时间换脑力...............(我是个蒟蒻,考场上啥都没想出来,就一个暴力,还就只有50)

30pts解法

根据题目特性可知,有三个点是有一个假密码,输出81就有30分(够水)  3

#include <bits/stdc++.h>
using namespace std;
int main() {
	//freopen("lock.in", "r", stdin);
	//freopen("lock.out", "w", stdout);
    //考试时别忘了加 freopen 哦
	cout << "81\n";
	return 0;
}
100pts解法

根据分析可知,实际上所有答案都不大于81,也就是一个假密码可以推出的真密码的数量。

我们随机选一个假密码作为基准,推出81个可能的密码。使时间复杂度大大减少。

如果考场上没想到就只能打暴力了,盲猜时间会爆

在去用其他的假密码检查,也就是列举所有可以转出假密码的状态,求其交集。就可以得到最终的正确答案。

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

int n, ans, lk[10][10], a[100][10]; //lk为n个假密码 a为81个可能是密码的集合 
void csh() {
	for (int i = 1; i <= 5; ++ i) { //五个轮每个都有9种情况 
		for (int j = 1; j <= 9; ++ j) {
			lk[1][i] ++; 
			lk[1][i] %= 10;   
			for (int k = 1; k <= 5; ++ k)
				a[(i - 1) * 9 + j][k] = lk[1][k];
		}
		
		lk[1][i] ++;
		lk[1][i] %= 10; //保证不超过一位数
	}
	
	for (int i = 1; i <= 4; ++ i) {
		for (int j = 1; j <= 9; ++ j) {
			lk[1][i] ++;
			lk[1][i + 1] ++;
			//一次转两个轮 
			lk[1][i] %= 10;//保证不超过一位数
			lk[1][i + 1] %= 10;//保证不超过一位数
			
			for (int k = 1; k <= 5; ++ k)
				a[(i + 4) * 9 + j][k] = lk[1][k];
		}
			
		lk[1][i] ++;
		lk[1][i + 1] ++;
		lk[1][i] %= 10;//保证不超过一位数
		lk[1][i + 1] %= 10;//保证不超过一位数
		//变成初始的样子 
	} 
	
	/*for (int i = 1; i <= 81; ++ i) {
		for (int j = 1; j <= 5; ++ j)
			cout << a[i][j] << " ";
		cout << "\n";
	}*/
}


int main() {
	//freopen("lock.in", "r", stdin);
	//freopen("lock.out", "w", stdout);
	
	cin >> n;
	for (int i = 1; i <= n; ++ i) {
		for (int j = 1; j <= 5; ++ j)
			cin >> lk[i][j];
	}
	
	if (n == 1) {
		cout << "81\n";
		return 0;
	}
	csh(); //把可能的81种答案存入 a数组再去一个一个匹配 
		
	for (int x = 2; x <= n; ++ x) { //枚举剩下的轮子 
		for (int lz = 1; lz <= 81; ++ lz) { //把81种查一遍 
			if (a[lz][1] == -1) continue;
			bool flag = false;
			for (int i = 1; i <= 5; ++ i) { //五个轮每个都有9种情况 
				for (int j = 1; j <= 9; ++ j) {
					lk[x][i] ++; 
					lk[x][i] %= 10; //为保证所有假密码不成为正确答案,先进性改变,再去匹配 
					
					bool ff = true;
					for (int k = 1; k <= 5; ++ k) {
						if(lk[x][k] != a[lz][k]) {
							ff = false;
							break;
						}
					}
					if (ff == true)
						flag = true; //如果找到了可以与第一个密码匹配的假密码,状态改为TRUE 并且到达下一步 
				}
				
				lk[x][i] ++;
				lk[x][i] %= 10; //保证不超过一位数
				if (flag == true) 
					break;
			}
			if (flag == true) {
				//for (int i = 1; i <= 5; ++ i)
				//	cout << lk[x][i] << " ";
				//cout << endl;
				continue;
			}
			
			for (int i = 1; i <= 4; ++ i) {
				for (int j = 1; j <= 9; ++ j) {
					lk[x][i] ++;
					lk[x][i + 1] ++;
					//一次转两个轮 
					lk[x][i] %= 10;//保证不超过一位数
					lk[x][i + 1] %= 10;//保证不超过一位数
					
					bool ff = true;
					for (int k = 1; k <= 5; ++ k) {
						if(lk[x][k] != a[lz][k]) {
							ff = false;
							break;
						}
					}
					if (ff == true)
						flag = true;
				}
					
				lk[x][i] ++;
				lk[x][i + 1] ++;
				lk[x][i] %= 10;//保证不超过一位数
				lk[x][i + 1] %= 10;//保证不超过一位数
				//变成初始的样子 
				if (flag == true) 
					break;
			} 
			if (flag == false) a[lz][1] = -1;
		}
	}
	
	for (int i = 1; i <= 81; ++ i) {
		if (a[i][1] != -1)
			ans ++;
	}
	
	cout << ans << "\n";
	return 0;
}
/*
2
2 6 6 8 6
2 6 9 1 3

2
*/

完结撒花

第一次CSP-S,还是蛮难的。加油,明年CSP考场再会!!

可以点个赞鼓励一下吗,求求啦 φ(>ω<*) 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值