• DFS——jzyzoj1187:虫食算


      卡了两天的虫食算,调了一天的错误代码。给出题面:

      

      没错题目很长,不过实际上就是给出三个n位数的字符串,每个大写字母都代表唯一的数字,让第一个字符串与第二个字符串相加,使得前两个字符串构成的N位数字相加后等于第三个字符串所代表的N位数字。按照字典序输出各个大写字母对应的值。

      暴力的方法非常简单。用简单的dfs+回溯搜索每一个大写字母出现的值然后相加(注意进制以及进位),如果最终结果符合条件的话退出即可。

      代码:

     1 //暴力
     2 #include <iostream>
     3 #include <cstdio>
     4 #include <algorithm>
     5 #include <string>
     6 #include <cstring>
     7 using namespace std;
     8 int num[30],n;
     9 char ch[4][30];
    10 bool f[30],flag=false;
    11 void dfs(int p)
    12 {
    13     if (p==n)
    14     {
    15         int sum=0,n1,n2,n3;
    16         for (int i=n-1;i>=0;i--)
    17         {
    18             n1=num[ch[1][i]-'A'];
    19             n2=num[ch[2][i]-'A'];
    20             n3=num[ch[3][i]-'A'];
    21             if ((n1+n2+sum)%n!=n3) return;
    22             sum=(n1+n2+sum)/n;
    23         }
    24         flag=true;
    25     }
    26     else 
    27     {
    28         for (int i=n-1;i>=0;i--)
    29         {
    30             if (!f[i]) 
    31             {
    32                 f[i]=true;
    33                 num[p]=i;
    34                 dfs(p+1);
    35                 if (flag) 
    36                 {
    37                     return;
    38                 }
    39                 f[i]=false;
    40             }
    41         }
    42     }
    43 }
    44 int main()
    45 {
    46     freopen("add.in","r",stdin);
    47     freopen("add.out","w",stdout);
    48     memset(f,false,sizeof(f));
    49     scanf("%d",&n);
    50     scanf("%s",&ch[1]);
    51     scanf("%s",&ch[2]);
    52     scanf("%s",&ch[3]);
    53     dfs(0);
    54     for (int i=0; i<n; i++)
    55     {
    56         printf("%d ",num[i]);
    57     }
    58     printf("
    ");
    59     return 0;
    60 }
    暴力

      但是数据范围是26....如果极限数据的话,26^26.....而且剪枝似乎很难...果断放弃搜索字母的方法

      所以还是后序遍历。。。

      从每个字符串的尾部开始枚举,如果1或2字符串的字符还没有出现过,就进行枚举,如果全部出现过的话,就进行特判,这样的算法看上去也是o(n^n),但是仔细想想:从尾字母开始,每个字符串的长度都是n,所以一定会有某个大写字母重复出现的情况,而且事实上这种情况会有很多,所以整体的时间复杂度就特别低。可以将90%的数据在1S之内得出答案。

      代码:

      1 //后序遍历(AC)
      2 #include<iostream>
      3 #include<cstdio>
      4 #include<string>
      5 #include<cstring>
      6 #include<algorithm>
      7 #include<cmath>
      8 int n,num[30],sum=0;
      9 char ch1[30],ch2[30],ch3[30];
     10 bool flag[30],flagnum[30],f=false;
     11 using namespace std;
     12 void dfs(int p)
     13 {
     14     if (p==-1)
     15     {
     16         f=true;    return;
     17     }
     18     if (flag[ch1[p]-'A']&&flag[ch2[p]-'A'])
     19     {
     20         int temp=sum;
     21         int n1=num[ch1[p]-'A'],n2=num[ch2[p]-'A'],n3;
     22         if (flag[ch3[p]-'A'])
     23         {
     24             n3=num[ch3[p]-'A'];
     25             if ((n1+n2+sum)%n!=n3)    return;
     26             sum=(n1+n2+sum)/n;
     27             dfs(p-1);
     28             if (f)    return;
     29             sum=temp;
     30         }
     31         else
     32         {
     33             n3=(n1+n2+sum)%n;
     34             sum=(n1+n2+sum)/n;
     35             if (flagnum[n3])
     36             {
     37                 sum=temp;
     38                 return;
     39             }
     40             flag[ch3[p]-'A']=true;
     41             flagnum[n3]=true;
     42             num[ch3[p]-'A']=n3;
     43             dfs(p-1);
     44             if (f)    return;
     45             flag[ch3[p]-'A']=false;
     46             flagnum[n3]=false;
     47             num[ch3[p]-'A']=-1;
     48             sum=temp;
     49         }
     50     }
     51     else if (flag[ch1[p]-'A']&&!flag[ch2[p]-'A'])
     52     {
     53         else 
     54         {
     55             for (int i=n-1;i>=0;i--)
     56             {
     57                 if (!flagnum[i])
     58                 {
     59                     flagnum[i]=true;
     60                     num[ch2[p]-'A']=i;
     61                     flag[ch2[p]-'A']=true;
     62                     dfs(p);
     63                     if (f)    return;
     64                     flagnum[i]=false;
     65                     num[ch2[p]-'A']=-1;
     66                     flag[ch2[p]-'A']=false;
     67                 }
     68             }
     69         }
     70     }
     71     else if (flag[ch2[p]-'A']&&!flag[ch1[p]-'A'])
     72     {
     73             for (int i=n-1;i>=0;i--)
     74             {
     75                 if (!flagnum[i])
     76                 {
     77                     flagnum[i]=true;
     78                     num[ch1[p]-'A']=i;
     79                     flag[ch1[p]-'A']=true;
     80                     dfs(p);
     81                     if (f)    return;
     82                     flagnum[i]=false;
     83                     num[ch1[p]-'A']=-1;
     84                     flag[ch1[p]-'A']=false;
     85                 }
     86             }
     87     }
     88     else 
     89     {
     90         for (int i=n-1;i>=0;i--)
     91             if (!flagnum[i]) 
     92             {
     93                 flagnum[i]=flag[ch1[p]-'A']=true;
     94                 num[ch1[p]-'A']=i;
     95                 dfs(p);
     96                 if (f) return;
     97                 num[ch1[p]-'A']=-1;
     98                 flagnum[i]=flag[ch1[p]-'A']=false;
     99             }
    100     }
    101 }
    102 int main()
    103 {
    104     //freopen("add.in","r",stdin);
    105     //freopen("add.out","w",stdout);
    106     memset(flag,false,sizeof(flag));
    107     memset(flagnum,false,sizeof(flagnum));
    108     memset(num,-1,sizeof(num));
    109     scanf("%d",&n);
    110     scanf("%s",&ch1);
    111     scanf("%s",&ch2);
    112     scanf("%s",&ch3);
    113     dfs(n-1);
    114     for (int i=0;i<n;i++)
    115         printf("%d ",num[i]);
    116     printf("
    ");
    117     return 0;
    118 }
    后序搜索

      但是其实这个也算是暴力的方法,还有几个剪枝条件,下面就不给出代码了,只有具体的思路。

      1.只要在某个地方,如果三个字符中有两个大写字母的值已经出现,那么另外一个字母的值是一定可以知道的。

      2.当三个字符中有一个字符代表0,且进位也是0时可以进行判断,如果剩下的两个字符不相等的话,无论怎么枚举都不可能得到正解。

      3.当一个加数与和中的字符相等时,如果进位是0,另一个加数必定是0。如果进位是1,另一个加数必定是n-1。

      大概还有别的剪枝条件....

      但是只需要这三个剪枝条件就能把程序的运行时间降到很短。

      这样的话大概可以对本题进行延伸:

        1.数据不一定有解,如果无解的话,输出-1。

        2.数据不一定有唯一解,如果有多个解,按照从A~A+n-1代表的数组成的一个n位数从大到小输出。

  • 相关阅读:
    input中的disabled 和 readonly的区别
    pwa-serviceWorker与页面通信postMessage
    PWA之push服务
    vue+typescript入门学习
    基于node 搭建http2服务
    阻止默认行为是配合passive使用
    正则表达式exec方法的陷阱
    serviceWorker-资料参考
    MVC的增删改和Razor
    MVC基础
  • 原文地址:https://www.cnblogs.com/hinanawitenshi/p/6439491.html
Copyright © 2020-2023  润新知