tyvj 1131:
搜索题,但是这题的重点在于减枝,下面是题解里写的:
对于一个测试数据
5
ABCED
BDACE
EBBAA
一:剪枝:
如最后一列的D,E,A
如果D,E,A的值都已经搜出来一种方案了,那么A =(D+E) mod n 即D+E除以n的余数是A,因为D+E有可能〉n并且进位,所以要mod n,详细一点即,对于搜索出来的D,E,A如果上一位进位则 A=(D+E+1)mod n,不进位则A=(D+E)mod n,不知道是否进位则(A=(D+E+1)mod n)or( A=(D+E)mod n),如果满足这些条件则继续,否则退出。
如果只知道D,E则(D+E)mod n(如进位则是(D+E+1)mod n)这个数没被赋值到其他的字母上去就可以继续搜,同样只知道E,A和D,A也可以这样剪枝。
E,A:[A-E mod n] (进位则是 (A-E-1) mod n)没被赋给除D外的其它字母
D,A:[A-D mod n] (进位则是 (A-D-1) mod n)没被赋给除E外的其它字母
二:优化:
还有一个优化就是从搜索顺序的优化:搜索顺序的改变也成为大大提高程序效率的关键:从右往左,按照字母出现顺序搜索,有很大程度上提高了先剪掉废枝的情况。
三:注意:
进位情况要特别注意:
(1)
如果D,E,A都知道,那么(D+E)DIV n<>0则进位否则不进(上一位进位则(D+E+1)DIV n<>0 )
(2)
如果知道D,E那么(D+E)DIV n<>0则进位否则不进(上一位进位则(D+E+1)DIV n<>0 ) 特殊情况:如果上一位不确定是否进位那么又要分情况讨论:○1如果进位,不进位两种情况中只有一种情况合法(即所确定的数符合剪枝条件),那么就按这种情况确定是不是进位。○2如果两种情况都合法则,如果两种情况的进位是否都相同,那么可以确定这一位是否进位,不同的话则进位状态赋值为待定。
(3)
知道A,E那么 A<E则进位否则不进(上一位进位则(A-1)<E ) 特殊情况:同(2)。
(4)
知道A,D那么 D<E则进位否则不进(上一位进位则(A-1)<D ) 特殊情况:同(2)。
(5)
只知道D,E,A中一个的值,则进位状态待定。
#include <stdio.h>
#include <string.h>
int n;
char s[3][30];
int let[27];//let[i]=a表示数字i代表的是a+'A'字母
int b[27], bef[27], x, flag;
//b[i]=a表示i+'A'字母代表数字a,bef数组存放的是字母出现的先后顺序,这样搜索效率比较高
int f()
{
int temp = 0, l = 0, i;//l为进位
for(i = n - 1; i >= 0; i--)
{
temp = (b[s[0][i] - 'A'] + b[s[1][i] - 'A'] + l) % n;
l = (b[s[0][i] - 'A'] + b[s[1][i] - 'A'] + l) / n;
if(temp != b[s[2][i] - 'A'])
return 0;
}
return 1;
}
int check()
{
int temp, t1, t2, t3, i;
for(i = n - 1; i >= 0; i--)
{
t1 = s[0][i] - 'A';
t2 = s[1][i] - 'A';
t3 = s[2][i] - 'A';
if(b[t1] != -1 && b[t2] != -1 && b[t3] != -1)
if((b[t1] + b[t2]) % n == b[t3] || (b[t1] + b[t2] + 1) % n == b[t3])//注意还要考虑有进位的情况
continue;
else
return 0;
if(b[t1] != -1 && b[t2] != -1)
{
temp = (b[t1] + b[t2]) % n;
//如果数字temp和temp+1(有进位)都已经被使用过就返回0
//有其中一个或两个没有被使用过,就说明b[t1]+b[t2]=temp可能成立
if(let[temp] == -1 || let[(temp + 1) % n] == -1)
continue;
else
return 0;
}
if(b[t1] != -1 && b[t3] != -1)
{
temp = b[t3] - b[t1];
if(temp >= 0 && let[temp] == -1)
continue;
temp += n;//如果temp是负数,那么就加上n再进行判断
if(temp >= 0 && let[temp] == -1)
continue;
temp = b[t3] - b[t1] - 1;//考虑进位
if(temp >= 0 && let[temp] == -1)
continue;
temp += n;
if(temp >= 0 && let[temp] == -1)
continue;
return 0;
}
if(b[t2] != -1 && b[t3] != -1)
{
temp = b[t3] - b[t2];
if(temp >= 0 && let[temp] == -1)
continue;
temp += n;
if(temp >= 0 && let[temp] == -1)
continue;
temp = b[t3] - b[t2] - 1;
if(temp >= 0 && let[temp] == -1)
continue;
temp += n;
if(temp >= 0 && let[temp] == -1)
continue;
return 0;
}
}
return 1;
}
void dfs(int k)
{
int i;
if(k == n)
{
if(f())
flag = 1;
return;
}
for(i = n - 1; i >= 0; i --)
{
if(let[i] == -1)
{//数字i还没有被用过
let[i] = bef[k];
b[bef[k]] = i;
int ok;
if(check())
dfs(k + 1);
if(flag)
return ;
let[i] = -1;
b[bef[k]] = -1;
}
}
}
int main (void)
{
while(scanf("%d", &n) != EOF)
{
scanf("%s%s%s", s[0], s[1], s[2]);
int i, j;
memset(b, 0, sizeof(b));
memset(let, -1, sizeof(let));
x = 0;
flag = 0;
for(i = n - 1; i >= 0; i--)
{
for(j = 0; j < 3; j++)
{
if(b[s[j][i] - 'A'] == 0)
{
b[s[j][i] - 'A'] = -1;
bef[x ++] = s[j][i] - 'A';
}
}
}
dfs(0);
for(i = 0; i < n; i++)
printf("%d ", b[i]);
}
return 0;
}
刚开始就是全部搜索完再判断是否成立,个人觉得应该是超时,但是为什么是WA啊= =还特意看了两组数据,程序跑出来的明明也对……顺便贴几个测试数据把……
8
ABCDEFAG
DEABCGED
BEEHBDBC
5 7 4 1 2 6 3 0
5
ABCED
BDACE
EBBAA
1 0 3 4 2
8
ABDEBEHG
FGDBBAGB
EDEDCHGE
2 1 3 7 6 4 5 0
21
BADCEFGHIJKLMNOPQRSTU
BADCEFGHIJKLMNOPQRSTU
BDEGIKMOQTBDEGIKMOQST
1 0 3 2 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 2