zoukankan      html  css  js  c++  java
  • [题解]洛谷P1092 虫食算

    原题:

      P1092

    思路:

      我太弱了,竟然打了一个上午。。。。。。

      一开始我竟然想从A到B枚举每一种排列最后检查。。。很显然这种方法效率特别低——O(N!)

      于是想到了由低位到高位,由两个加数上的位到和上的位,逐个枚举数字。当然如果不剪枝的话就和第一种方法没什么区别

      

    剪枝:

    剪枝一:

     这个剪枝比较好想,在每枚举完一列时(或者该列上的数字已被枚举了),检查上两行(加数位)之和加上上一位的进位对进制数取模是否等于最后一行(两数之和的位),如果不相等就说明当前状态无法得出正解,可以剪枝。

    剪枝二:

      这个有点难想到。

      众所周知,加法中进位最多为1(小学数学知识)

      那么对于每一位,如果该位上的三个数都被枚举过了,记第一行数为a,第二行为b,第三行为c,则可以分以下两种情况讨论:

    1. 上一位无进位,则a+b==c
    2. 上一位有进位,则a+b+1==c

      如果对于以上两种情况均不成立,则说明当前状态无法得出正解,可以剪枝。

       (如果有不理解的可以问我或者结合代码注释理解)

    上代码:

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    using namespace std;
    
    int n,ans[30],use[30]={0};
    char s[4][30];
    
    //给每一个编号 
    int id(char ch){
        return ch-'A'+1;
    }
    
    //输出结果 
    void print(){
        for(int i=1;i<=n;i++)
            printf("%d ",ans[i]);
    }
    
    void dfs(int x,int y,int z){
        if(x==0&&z==0){
            print();
            exit(0);//找到答案直接滚粗 
        }
        else{
            
            //剪枝二 
            /*那么对于每一位,如果该位上的三个数都被枚举过了,记第一行数为a,第二行为b,第三行为c,则可以分以下两种情况讨论:
            上一位无进位,则a+b==c
            上一位有进位,则a+b+1==c*/
            int a,b,c;
            for(int i=x-1;i>0;i--){
                a=ans[id(s[1][i])];b=ans[id(s[2][i])];c=ans[id(s[3][i])];
                if(a!=-1&&b!=-1&&c!=-1&&(a+b)%n!=c&&(a+b+1)%n!=c)
                    return;
            }
            
            if(ans[id(s[y][x])]==-1){
                //前两行 
                if(y<3){
                    for(int i=0;i<n;i++){
                        if(!use[i]){
                            ans[id(s[y][x])]=i;use[i]=1;
                            dfs(x,y+1,z);
                            ans[id(s[y][x])]=-1;use[i]=0;
                        }
                    }
                }
                //第三行 
                if(y==3){
                    for(int i=0;i<n;i++){
                        if(!use[i]){
                            int zz=ans[id(s[1][x])]+ans[id(s[2][x])]+z;
                            //剪枝一 
                            if(zz%n==i){     
                                ans[id(s[y][x])]=i;use[i]=1;
                                dfs(x-1,1,zz/n);
                                ans[id(s[y][x])]=-1;use[i]=0;
                            }
                        }
                    }
                }
            }
            else{
                //前两行 
                if(y<3){
                    dfs(x,y+1,z);
                }
                //第三行 
                if(y==3){
                    int zz=ans[id(s[1][x])]+ans[id(s[2][x])]+z;
                    //剪枝一 
                    if(zz%n==ans[id(s[3][x])]){   
                        dfs(x-1,1,zz/n);
                    }
                }
            }
        }
    } 
    
    int main(){
        memset(ans,-1,sizeof(ans));//初始化为-1,因为每一个数也可以是0 
        scanf("%d",&n);
        scanf("%s%s%s",s[1]+1,s[2]+1,s[3]+1);
        dfs(n,1,0);//从最低位的第一个加数开始枚举 
        return 0;
    } 

      施工Van♂毕!ヾ(゚∀゚ゞ)~~~~~~~

    本篇文章为SHINE_GEEK原创,转载请注明来源!
    written_by:SHINE_GEEK

    -------------------------------------
    签名:自己选的路,跪着也要走完;理想的实现,需要不懈奋斗!
    -------------------------------------
  • 相关阅读:
    WeihanLi.Npoi 1.10.0 更新日志
    消除代码中的坏味道,编写高质量代码
    代码重构之法——方法重构分析
    使用 C# 捕获进程输出
    .net core 中的经典设计模式的应用
    JDBC 规范中文版 4.2 -第一章 简介
    基础回顾-线程的几种状态
    一文读懂BeanFactory和FactoryBean区别
    阿里云云计算ACA 第三章 阿里云存储服务
    阿里云云计算ACA 第二章 阿里云弹性计算
  • 原文地址:https://www.cnblogs.com/sjrb/p/10293129.html
Copyright © 2011-2022 走看看