[CSP-S 2023] 密码锁

发布时间 2023-12-23 16:01:21作者: xinyimama

题目描述

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

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

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

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

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

输入格式

输入的第一行包含一个正整数 \(n\),表示锁车后密码锁的状态数。

接下来 \(n\) 行每行包含五个整数,表示一个密码锁的状态。

输出格式

输出一行包含一个整数,表示密码锁的这 \(n\) 个状态按照给定的锁车方式能对应多少种正确密码。

样例 #1

样例输入 #1

1
0 0 1 1 5

样例输出 #1

81

提示

【样例 1 解释】

一共有 \(81\) 种可能的方案。

其中转动一个拨圈的方案有 \(45\) 种,转动两个拨圈的方案有 \(36\) 种。

【数据范围】

对于所有测试数据有:\(1 \leq n \leq 8\)

测试点 \(n\leq\) 特殊性质
\(1\sim 3\) \(1\)
\(4\sim 5\) \(2\)
\(6\sim 8\) \(8\) A
\(9\sim 10\) \(8\)

特殊性质 A:保证所有正确密码都可以通过仅转动一个拨圈得到测试数据给出的 \(n\) 个状态。

题解

刚开始看到这个题,橙题,我应该能做,发现如果n等于1的时候,答案肯定是81,但是当n比较大的时候,不知道该怎么做了?一直在想,他有什么样的性质才能这样?

但是,我一直有个感觉,这个题可以搜索,为什么呢?因为最多有5位密码,后来换了思路,我们搜索得到所有可能的状态,依次判断这种状态是否能通过拨圈达到题目中说的状态,这样的时间复杂度是O(100000),判断的时间复杂度为5n,所以最终的时间复杂是O(500000n)。

枚举的代码非常好写,但是判断的代码不好写,譬如。5 9会变成6 0,7 1,8 2,9 3,0 4,1 5,2 6,3 7,4 8共9种状态,我们发现他们的差是一样的,但是有个问题,9会变0,这个怎么处理?我最终的处理方法是判断两者之间的大小关系,如果发生变化,把小的数字加上10,从而保持原来的大小关系,代码如下:

点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n,a[15][15],ans;
int c[10];
bool check(){
    for(int i=1;i<=n;i++){
        bool b=true;//是否转动过拨圈
        int f;
        for(int j=1;j<=5;j++){
            if(c[j]==a[i][j]) continue;
            else{
                if(b==false)
                    return false;
                if(c[j+1]!=a[i][j+1]&&j<=4){
                    if(c[j]<c[j+1]){
                        f=a[i][j+1];
                        if(a[i][j+1]<a[i][j]) f=a[i][j+1]+10;
                        if(f-a[i][j]!=c[j+1]-c[j]) return false;
                        j++;
                        b=false;
                        continue;
                    }
                    if(c[j+1]==c[j]){
                        if(a[i][j+1]!=a[i][j]) return false;
                        j++;
                        b=false;
                        continue;
                    }
                    if(c[j+1]<c[j]){
                        f=a[i][j];
                        if(a[i][j]<a[i][j+1]) f=a[i][j]+10;
                        if(f-a[i][j+1]!=c[j]-c[j+1]) return false;
                        j++;
                        b=false;
                        continue;
                    }
                }
                if((c[j+1]==a[i][j+1]&&j<=4)||j==5){
                    b=false;//已经转动过一次拨圈
                }
            }
        }
        if(b==true) return false;
    }
    return true;
}

void dfs(int k){
    if(k==6){
        if(check()) {
            ans++;
            //for(int i=1;i<=5;i++) cout<<c[i]<<" ";
            //cout<<endl;
        }
        return;
    }
    for(int i=0;i<=9;i++){
        c[k]=i;
        dfs(k+1);
        c[k]=0;
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=5;j++){
            scanf("%d",&a[i][j]);
        }
    }
    dfs(1);
    cout<<ans<<endl;
    return 0;
}