USACO Section1.3 Combination Lock

题目

农夫约翰的奶牛不停地从他的农场中逃出来,导致了很多损害。为了防止它们再逃出来,他买了一只很大的号码锁以防止奶牛们打开牧场的门。
农夫约翰知道他的奶牛很聪明,所以他希望确保它们不会在简单地试了很多不同的号码组合之后就能轻易开锁。锁上有三个转盘,每个上面有数字1..N (1 <= N <= 100),因为转盘是圆的,所以1和N是相邻的。有两种能开锁的号码组合,一种是农夫约翰设定的,还有一种“预设”号码组合是锁匠设定的。但是,锁有一定的容错性,所以,在每个转盘上的数字都与一个合法的号码组合中相应的数字相距两个位置以内时,锁也会打开。
比如说,如果农夫约翰的号码组合是(1,2,3),预设号码组合是(4,5,6),在转盘被设定为(1,4,5)(因为这和农夫约翰的号码组合足够接近)或(2,4,8)(因为这和预设号码组合足够接近)。注意,(1,5,6)并不会打开锁,因为它与任一号码组合都不够接近。
给出农夫约翰的号码组合和预设号码组合,请计算能够开锁的不同的号码组合的数目。号码是有序的,所以(1,2,3)与(3,2,1)不同。

格式

输入:

第一行:整数N。
第二行:三个以空格分隔的整数,为农夫约翰的号码组合。
第三行:三个以空格分隔的整数,为预设号码组合(可能与农夫约翰的号码组合相同)。

输出:

第一行:所有不同的能够开锁的号码组合的总数。

示例:

输入:

50
1 2 3
5 6 7

输出:

249

题解

题意是每一位数字只要与对应的密码数字相差不超过2格,即可开锁。例如密码为1 2 3,因为是圆盘,如果最大数为50,则第一个数字可为49 50 1 2 3这五个数字,第二个数字可为 50 1 2 3 4,第三个数字可为 1 2 3 4 5,当然如果n=1,则 第一,二,三个数字都可为 1 1 1 1 1,所以需考虑重复的情况。因为有三个数字,两组密码,所以定义了2个结构体数组,再用三重循环即可算出所有可能的密码组合,因为是圆盘,n和1是连在一起的,所以我将圆盘拉长成一个数组c,并且将前四项复制串连在数组尾部,然后从(i=3;i<=n+2;i++)开始循环,这样即可确保不会越界。接着将每一个密码数字转换成在圆盘数组c中对应的下标,这样可方便三重循环。然后就是开始循环得出所有的可能组合,但是会出现重复的情况,所以需去除结构体数组中重复的组数,对此我是将重复的组数的三个元素全部赋值为0,因为正常的密码数字不包括0,然后对另一个结构体数组重复这样的操作,最后再用y存储这两个结构体数组中相同的结构体的数目,然后用两个结构体数组的总组数减去y,即为最后的密码组合的总数。

代码

/*
ID: a4556762
PROG: combo
LANG: C++
*/
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
struct mima
{
    int a;
    int b;
    int e;
};
int main()
{
    freopen("combo.in","r",stdin);
    freopen("combo.out","w",stdout);
    struct mima d[10000],f[10000];
    int i,j,k,n,x[7],t=0,m=0,y=0,c[200]={0},g=0,h=0;
    cin>>n;
    for(i=1;i<=n;i++)
        c[i]=i;
    for(i=1;i<=4;i++)
        c[i+n]=c[i];
    for(i=1;i<=6;i++)
    {
        cin>>x[i];
        for(j=3;j<=n+2;j++)
            if(x[i]==c[j])
                x[i]=j;
    }
    for(i=x[1]-2;i<=x[1]+2;i++)
        for(j=x[2]-2;j<=x[2]+2;j++)
            for(k=x[3]-2;k<=x[3]+2;k++)
            {
                d[t].a=c[i];
                d[t].b=c[j];
                d[t].e=c[k];
                t++;
            }
    for(i=0;i<t;i++)
        for(j=i+1;j<t;j++)
            if(d[i].a==d[j].a&&d[i].b==d[j].b&&d[i].e==d[j].e)
            {
                d[j].a=0;
                d[j].b=0;
                d[j].e=0;
            }
    g=t;
    for(i=0;i<g;i++)
        if(d[i].a==0&&d[i].b==0&&d[i].e==0)
            t--;
    for(i=x[4]-2;i<=x[4]+2;i++)
        for(j=x[5]-2;j<=x[5]+2;j++)
            for(k=x[6]-2;k<=x[6]+2;k++)
            {
                f[m].a=c[i];
                f[m].b=c[j];
                f[m].e=c[k];
                m++;
            }
    for(i=0;i<m;i++)
        for(j=i+1;j<m;j++)
            if(f[i].a==f[j].a&&f[i].b==f[j].b&&f[i].e==f[j].e)
            {
                f[j].a=0;
                f[j].b=0;
                f[j].e=0;
            }
    h=m;
    for(i=0;i<h;i++)
        if(f[i].a==0&&f[i].b==0&&f[i].e==0)
            m--;
    for(i=0;i<g;i++)
        for(j=0;j<h;j++)
            if(d[i].a!=0&&d[i].a==f[j].a&&d[i].b==f[j].b&&d[i].e==f[j].e)
                y++;
    cout<<m+t-y<<endl;
    return 0;
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值