题意简述
5 位密码锁,每一位是 0‑9 环形(9 下一个是 0)。锁车操作:从正确密码出发,恰好执行一次操作,操作二选一:
转动某 1 个拨圈任意幅度;
转动一对相邻拨圈,两个拨圈转动幅度完全相同。
现在给出 n 个锁车后的状态(全部不等于正确密码)。求:有多少种 5 位密码,满足:给出的每一个状态,都可以由该密码通过恰好一次合法锁车操作得到。
数据范围:1<=n<=8。
题目分析
由于总密码空间=100000,可以暴力枚举全部候选密码。
核心函数详细解释
cpp
bool check(int a[],int b[][5])
{
// 遍历每一个给出的锁后状态
for(int i=0;i<n;i++)
{
int s=0;
//统计a(正确密码)与b[i](锁后状态)有多少位不一样
for(int j=0;j<5;j++)
{
if(b[i][j]!=a[j]) s++;
}
// s=0:等于原密码,题目说锁后状态不能是正确密码;s>2超过最多改动2位,直接false
if(s>2||s==0) return 0;
if(s==1) continue; //只改1位,合法,下一个状态
// s==2:恰好两位不同,必须是相邻,偏移模10相等
for(int j=0;j<5;j++)
{
if(b[i][j]!=a[j])
{
// j位不同,j+1必须也要不同,否则不是相邻两位
if(b[i][j+1]==a[j+1]) return 0;
// 计算两个位置的偏移模10,必须相等
else if((b[i][j]-a[j]+10)%10==(b[i][j+1]-a[j+1]+10)%10)
{
break; //满足条件,退出j循环,该状态合法
}
else return 0; //偏移不等,非法
}
}
}
return true; //n个状态全部校验通过,这是一个合法正确密码
}
注意:j只会找到第一个不一样的下标,要求j与j+1同时不一样,偏移相等,就满足 "相邻两位改动、偏移相同"。
完整AC代码
cpp
#include <bits/stdc++.h>
using namespace std;
int n,a[6],b[10][5],ans=0;
bool check(int a[],int b[][5])
{
for(int i=0;i<n;i++)
{
int s=0;
for(int j=0;j<5;j++)
{
if(b[i][j]!=a[j]) s++;
}
if(s>2||s==0) return 0;
if(s==1) continue;
for(int j=0;j<5;j++)
{
if(b[i][j]!=a[j])
{
if(b[i][j+1]==a[j+1]) return 0;
else if((b[i][j]-a[j]+10)%10==(b[i][j+1]-a[j+1]+10)%10)
{
break;
}
else return 0;
}
}
}
return true;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<=4;j++)
{
cin>>b[i][j];
}
}
for(int i=100000;i<=199999;i++)//这里有一个枚举小技巧:如果不想五重循环的话,可以这样做。
{
int x=i;
for(int j=4;j>=0;j--)
{
a[j]=x%10;
x/=10;
}
if(check(a,b))
{
ans++;
}
}
cout<<ans;
return 0;
}