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位数从大到小输出。

原文地址:https://www.cnblogs.com/hinanawitenshi/p/6439491.html