• [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++

  • 相关阅读:
    SSM整合初级 简单的增删改查
    初始SpringMVC
    初始Spring MVC
    Web Service简介
    Quartz框架 实现任务调度
    搭建james邮件服务器
    使用Jdbc Template的基本操作步骤
    spring AOP四种实现方式
    Struts2数据校验
    Struts2 与Servlet API解耦,耦合的访问方式
  • 原文地址:https://www.cnblogs.com/wzc521/p/11137584.html
Copyright © 2020-2023  润新知