USACO-1.3:Combination Lock

本文介绍了USACO编程竞赛中的一个问题——组合锁,讲述了如何解决一个包含三个数字的环形锁,当数字在一定范围内变化时,有多少种方式可以打开锁。作者分享了题目背景、解题思路,并提供了代码实现,讨论了小于或等于5和大于5两种情况下的解决方案,重点在于找出重合部分的组合数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

感觉有一年没有做ACM的题目了,以前熟悉的一些算法也都忘得差不多了。感觉就是大概知道自己以前会过。甚至已经有半年多没有真正地写代码了,就想着回头把USACO做完吧。突然着手编程,感觉有点吃力。这算是一个比较简单的题目,但是还是花了一个下午加一个上午的时间才搞定。
题目链接

题目大意:

一个农夫想锁住它的牛,设置了一个有三个数字的锁。牛要打开这个锁就能逃出去。给定一个数字N,锁上的每个数字可以是1~N。这些锁是环形的,因为锁不太准确,所以数字差两位以内也是可以打开锁的。比如N是50的话,其中一个数字锁设定的数字为1那么1, 2, 3, 50. 49都能打开这把锁。这把锁一开始设置了两个密码,一个是农夫设的,一个是锁匠设的。现在给你这个数字N和这两个密码,问你一共有几种情况牛可以打开锁出去。
比如N=50
农夫设的密码是123
锁匠设的密码是567
那么输出就是249,表示有249种组合可以打开这把锁。
注意,这个密码必须是接近其中一个人比如234,这个密码每个数字和农夫设置的密码都在2以内所以可以打开锁。456则是接近锁匠的密码可以打开这个锁。但是254这个密码不接近任何一个密码,因此不能打开这个锁。注意:它是循环的,所以49 50 1这个密码和农夫的也很接近,可以打开这把锁。

解题思路:

如果这个数字锁,小于或者等于5那么可以知道,任何的数字都是接近的(因为是循环的)。因此开锁的方法就是n*n*n种。
如果数字锁是大于5的,那么接近农夫和接近锁匠的数字组合分别都有125种,总共打开锁的组合就有250种。但是还要考虑其中重合的组合,比如上面的例子中345这个组合就是重合的,因此最后的答案是249种。所以,这道题我们就是需要计算它们重合的部分了。重合的部分很明显,就是这把锁三个数字分别重合的情况相乘。而每个数字的重合情况就是考虑它们之间的差。具体的参考一下代码应该就能明白了。

我的代码:

/*
  ID:sunexio2
  LANG:C++
  TASK:combo
 */

#include <iostream>
#include <fstream>
#include <cmath>
using namespace std;

ifstream fin("combo.in");
ofstream fout("combo.out");

int calRecom(int *a, int *b, int n){
    int c[3] = {0, 0, 0};
    for(int i = 0; i < 3; ++i){
        int tmp = abs(a[i] - b[i]);
        if(tmp > 4 && tmp < n - 4)
            return 0;
        if(tmp == 4)
            c[i]++;
        else if(tmp == 3)
            c[i] += 2;
        else if(tmp == 2)
            c[i] += 3;
        else if(tmp == 1)
            c[i] += 4;
        else if(tmp == 0)
            c[i] += 5;
        else if(tmp == n - 1)
            c[i] += 4;
        else if(tmp == n -2)
            c[i] += 3;
        else if(tmp == n - 3)
            c[i] += 2;
        else if(tmp == n - 4)
            c[i]++;
    }
    return c[0] * c[1] * c[2];
}

int main(){
    int n;
    int fj_com[3], ms_com[3];
    while(fin >> n){
        for(int i = 0; i < 3; ++i)
            fin >> fj_com[i];
        for(int i = 0; i < 3; ++i)
            fin >> ms_com[i];
        if(n <= 5)
            fout << n * n * n << endl;
        else{
            int res = 250;
            int reCom = calRecom(fj_com, ms_com, n);
            fout << res - reCom << endl;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值