题目描述
小 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 个状态。
输入格式
输入的第一行包含一个正整数 n,表示锁车后密码锁的状态数。
接下来 n 行每行包含五个整数,表示一个密码锁的状态。
输出格式
输出一行包含一个整数,表示密码锁的这 n 个状态按照给定的锁车方式能对应多少种正确密码。
输入输出样例
输入 #1复制
1 0 0 1 1 5输出 #1复制
81说明/提示
【样例 1 解释】
一共有 81 种可能的方案。
其中转动一个拨圈的方案有 45 种,转动两个拨圈的方案有 36 种。
【样例 2】
见选手目录下的 lock/lock2.in 与 lock/lock2.ans。
【数据范围】
对于所有测试数据有:1≤n≤8。
测试点 n≤ 特殊性质 1∼3 1 无 4∼5 2 无 6∼8 8 A 9∼10 8 无 特殊性质 A:保证所有正确密码都可以通过仅转动一个拨圈得到测试数据给出的 n 个状态。
附件下载
lock.zip578B
代码:
#include <bits/stdc++.h>
using namespace std;
int n,sum;
int a[10][10];
int b[10];
bool check(){
for(int i = 1;i<=n;i++){
vector<int> v;//存当前密码与某个状态不一样的数字的个数
for(int j = 1;j<=5;j++){
if(a[i][j]!=b[j]) v.push_back(j);
//如果两个不相等,放进去,放进去的是这个数字的位置
}
//判断非法状态,留下合法状态
if(v.size()==0) return false;
//如果某个状态和当前密码相同,这个密码非法,因为给的状态都不是正确密码
if(v.size()>=3) return false;
//如果不同数字的个数大于3个,当前密码非法,因为一次只能同时转动一个或两个拨圈
if(v.size()==1) continue;
//如果只有一位不一样,可以拨到,看下一个状态
if(v.size()==2){
if(v[1]-v[0]>=2) return false;
//如果不是相邻的拨圈,就不可能拨到这个状态
else{
int sum1=a[i][v[0]]-b[v[0]];
//计算从某个数字状态到当前密码的差值,可正可负
int sum2=a[i][v[1]]-b[v[1]];
int ned1=(sum1%10+10)%10;
//计算需要拨动的幅度,这样写可以保证一定为正值
int ned2=(sum2%10+10)%10;
if(ned1!=ned2) return false;
//如果两个数字拨动的幅度不一样,不合法
}
}
}
return true;
}
void dfs(int now){//now代表现在是第几位
if(now>5){//如果now>5说明看到第六位了不需要看
if(check()) sum++;//check函数检查这个密码是否是正确密码
return ;
}else{
for(int i = 0;i<=9;i++){//枚举所有密码的情况
b[now]=i;
dfs(now+1);
}
}
}
int main(){
cin >> n;
for(int i = 1;i<=n;i++){
for(int j = 1;j<=5;j++){
cin >> a[i][j];//读入每一种状态
}
}
dfs(1);//深搜所有情况,也可以写五重循环
cout << sum;
return 0;
}