zoukankan      html  css  js  c++  java
  • [BZOJ1902]:[NOIP2004]虫食算(搜索)

    题目传送门


    题目描述

    所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。

    来看一个简单的例子:

      43#98650#45+8468#6633=44445506978

    其中#号代表被虫子啃掉的数字。根据算式,我们很容易判断:第一行的两个数字分别是53,第二行的数字是5。 现在,我们对问题做两个限制: 首先,我们只考虑加法的虫食算。这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0。 其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的。我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。如果这个算式是N进制的,我们就取英文字母表中的前N个大写字母来表示这个算式中的0N-1N个不同的数字(但是这N个字母并不一定顺序地代表0N-1)。输入数据保证N个字母分别至少出现一次。

        BADC+CBDA=DCCC

    上面的算式是一个4进制的算式。很显然,我们只要让ABCD分别代表0123,便可以让这个式子成立了。你的任务是,对于给定的N进制加法算式,求出N个不同的字母分别代表的数字,使得该加法算式成立。输入数据保证有且仅有一组解。


    输入格式

    包含4行。第一行有一个正整数N,后面的3行每行有一个由大写字母组成的字符串,分别代表两个加数以及和。这3个字符串左右两端都没有空格,从高位到低位,并且恰好有N位。


    输出格式

    包含一行。在这一行中,应当包含唯一的那组解。解是这样表示的:输出N个数字,分别表示A,B,C……所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。


    样例

    样例输入:

    5
    ABCED
    BDACE
    EBBAA

    样例输出:

    1 0 3 4 2


    数据范围与提示

    对于全部的数据,保证有N≤26


    题解

    官方正解是高斯消元,不得不承认高斯消元用来做这道题的确要比搜索简单。

    先来算一下时间复杂度,高斯消元:O(${n}^{3}$),搜索:O(${n}^{n}$)

    不管你怎么说,我感觉${n}^{n}$挺帅的。

    所以我就去搜索了哇~

    显然不能直接爆搜,卡长卡的好没准能跑过n=9n≥10想都别想了。

    那么考虑剪枝:

    为简便,地一个字符串称为A,第二个字符串称为B,第三个字符串称为C

    首先你肯定会想到,如果A+B肯定不能得到C,那么就不要往下搜了。

    这里需要注意的是,有可能会存在进位。

    然后接着考虑,小学的时候数学老师教给你竖式加法的时候,我们都是从最右边网左算。

    那么转换到这道题,我们也可以从右往左搜,这样就能更快的把枝剪掉了(注意你的judge函数要优秀)。

    还有一种情况,就是如果最前面出现了进位,那么是要剪掉的,所以我们先搜大的数可以更快的出现进位,进而更快的剪掉这个枝。

    所以从大往小搜必不可少。

    有了这些我就可以轻松A掉这道题了,但是作为一名卓越的OIER,我们还要考虑怎么让代码跑的更快。

    所以我就讲一下所有的剪枝:

    1.最前面出现进位直接return false,或者AB串开头位相加比C串开头位大,也要return false

    2.从左往右开始judge,首先,在A,B,C串都没有出现有字母没有赋值的情况的时候,可以按照严格的带进位的加法进行判断。

    3.在出现了一个数没有赋值之后,我们就要按照考虑进位与不进位开始考虑了。

      $alpha$.最好想到的就是无论进不进位都无法满足的话,就return false

      $eta$.如果当前位有两个数已经赋值,一个数还没有赋值,那么如果根据这两个数所算出来的第三个数已经被使用(赋值不能重复),那么可以提前剪掉。

    如果这些都能满足的话,则继续往前搜。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    bool vis[27];
    int wzc[27];
    int ans[27];
    int n;
    int a[27],b[27],c[27];
    int sor[27],cnt;
    char ch1[27],ch2[27],ch3[27];
    inline bool _wzc()//judge
    {
        int wzc=0,_=0;
        if(ans[a[0]]+ans[b[0]]>=n)return 0;//判断开头进不进位
        if(ans[c[0]]!=-1)//还是在开头剪枝
        {
            if(ans[a[0]]>ans[c[0]]||ans[b[0]]>ans[c[0]])return 0;
            if(ans[a[0]]!=-1&&ans[b[0]]!=-1&&ans[a[0]]+ans[b[0]]!=ans[c[0]]&&ans[a[0]]+ans[b[0]]+1!=ans[c[0]])return 0;
        }
        for(int i=n-1;i>=0;i--)//全都被赋过值
        {
            if(ans[a[i]]==-1||ans[b[i]]==-1||ans[c[i]]==-1)//出现有没有被赋过值的
            {
                _=i;
                break;
            }
            if((ans[a[i]]+ans[b[i]]+wzc)%n!=ans[c[i]])return 0;//严格带进未搜索
            wzc=(ans[a[i]]+ans[b[i]]+wzc)/n;
        }
        for(int i=_;i>=0;i--)//开始考虑进位与不进位,以下分情况,注意代码中“!=”和“==”。
        {
            if(ans[a[i]]!=-1&&ans[b[i]]!=-1&&ans[c[i]]!=-1)
                if((ans[a[i]]+ans[b[i]])%n!=ans[c[i]]&&(ans[a[i]]+ans[b[i]]+1)%n!=ans[c[i]])return 0;
            if(ans[a[i]]!=-1&&ans[b[i]]!=-1&&ans[c[i]]==-1)
                if((vis[(ans[a[i]]+ans[b[i]])%n])&&vis[(ans[a[i]]+ans[b[i]]+1)%n])return 0;
            if(ans[a[i]]!=-1&&ans[b[i]]==-1&&ans[c[i]]!=-1)
                if((vis[ans[c[i]]-ans[a[i]]])&&vis[ans[c[i]]+ans[a[i]]-1]&&vis[c[i]+n-ans[a[i]]]&&vis[c[i]+n-ans[a[i]]-1])return 0;
            if(ans[a[i]]==-1&&ans[b[i]]!=-1&&ans[c[i]]!=-1)
                if((vis[ans[c[i]]-ans[a[i]]])&&vis[ans[c[i]]+ans[a[i]]-1]&&vis[c[i]+n-ans[a[i]]]&&vis[c[i]+n-ans[a[i]]-1])return 0;
        }
        return 1;
    }
    void dfs(int x)
    {
        if(!_wzc())return;
        if(x==n)
        {
            if(_wzc())
            {
        		for(int i=0;i<n;i++)
        		    printf("%d ",ans[i]);
        		exit(0);//搜到结果直接结束
            }
        }
        for(int i=n-1;i>=0;i--)//反向搜
        {
            if(!vis[i])
            {
                vis[i]=1;
                ans[sor[x]]=i;
                dfs(x+1);
                vis[i]=0;
                ans[sor[x]]=-1;
            }
        }
        return;
    }
    int main()
    {
        scanf("%d",&n);
        scanf("%s%s%s",ch1,ch2,ch3);
        for(int i=0;i<n;i++)
        {
            a[i]=ch1[i]-'A';
            b[i]=ch2[i]-'A';
            c[i]=ch3[i]-'A';
            ans[i]=-1;
        }
        for(int i=n-1;i>=0;i--)//统计搜索顺序
        {	
            if(!vis[a[i]])
            {	
                vis[a[i]]=1;
                sor[cnt++]=a[i];
            }
            if(!vis[b[i]])
            {
                vis[b[i]]=1;
                sor[cnt++]=b[i];
            }
            if(!vis[c[i]])
            {
                vis[c[i]]=1;
                sor[cnt++]=c[i];
            }
        }
        memset(vis,0,sizeof(vis));
        dfs(0);
        return 0;
    }
    

    rp++

  • 相关阅读:
    LeetCode Best Time to Buy and Sell Stock
    LeetCode Scramble String
    LeetCode Search in Rotated Sorted Array II
    LeetCode Gas Station
    LeetCode Insertion Sort List
    LeetCode Maximal Rectangle
    Oracle procedure
    浏览器下载代码
    Shell check IP
    KVM- 存储池配置
  • 原文地址:https://www.cnblogs.com/wzc521/p/11137584.html
Copyright © 2011-2022 走看看