虫食算题目出处(3703)
此题的直接思路是搜索,但搜索得有技术(其实就是剪枝),不然会超时
膜拜自己
这道题从个位往最高位搜,就可以避免进位的重复运算。。。。。。
最后check一下,然后递归输出
先上代码,后面再来分段讨论
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
bool flag[95];
char a[30],b[30],c[30],d[30];
int v[95],n;
inline void ycl() {
int tot=0;
for(int i=n-1;i>=0;i--) {
if(!flag[a[i]]) {
flag[a[i]]=1;
d[++tot]=a[i];
}
if(!flag[b[i]]) {
flag[b[i]]=1;
d[++tot]=b[i];
}
if(!flag[c[i]]) {
flag[c[i]]=1;
d[++tot]=c[i];
}
}
memset(flag,0,sizeof(flag));
memset(v,-1,sizeof(v));
}
inline bool ok() {
int jw=0;
for(int i=n-1;i>=0;i--) {
if((v[a[i]]+v[b[i]]+jw)%n!=v[c[i]])
return 0;
jw=(v[a[i]]+v[b[i]]+jw)/n;
}
if(jw)
return 0;
return 1;
}
inline void pr() {
for(int i=65;i<=64+n;i++)
if(v[i]!=-1)
printf("%d ",v[i]);
exit(0);
}
inline bool check() {
for(int i=n-1;i>=0;i--)
if(v[a[i]]!=-1&&v[b[i]]!=-1&&v[c[i]]!=-1)
if((v[a[i]]+v[b[i]])%n!=v[c[i]]&&(v[a[i]]+v[b[i]]+1)%n!=v[c[i]])
return 0;
else if(v[a[i]]!=-1&&v[b[i]]!=-1&&v[c[i]]==-1)
if(flag[(v[a[i]]+v[b[i]])%n]&&flag[(v[a[i]]+v[b[i]]+1)%n])
return 0;
else if(v[a[i]]!=-1&&v[b[i]]==-1&&v[c[i]]!=-1)
if(flag[(v[c[i]]-v[a[i]]+n)%n]&&flag[(v[c[i]]-v[a[i]]-1+n)%n])
return 0;
else if(v[a[i]]==-1&&v[b[i]]!=-1&&v[c[i]]!=-1)
if(flag[(v[c[i]]-v[b[i]]+n)%n]&&flag[(v[c[i]]-v[b[i]]-1+n)%n])
return 0;
return 1;
}
inline void dfs(int i) {
if(i>n) {
if(ok())
pr();
return;
}
for(int j=n-1;j>=0;j--) {
if(flag[j])
continue;
v[d[i]]=j;
flag[j]=1;
if(!check()) {
v[d[i]]=-1;
flag[j]=0;
continue;
}
dfs(i+1);
v[d[i]]=-1;
flag[j]=0;
}
}
int main() {
scanf("%d\n",&n);
gets(a);
gets(b);
gets(c);
ycl();
dfs(1);
}
主函数的代码很好懂,我们就直接看dfs:
inline void dfs(int i) {
if(i>n) {
if(ok())
pr();
return;
}
for(int j=n-1;j>=0;j--) {
if(flag[j])
continue;
v[d[i]]=j;
flag[j]=1;
if(!check()) {
v[d[i]]=-1;
flag[j]=0;
continue;
}
dfs(i+1);
v[d[i]]=-1;
flag[j]=0;
}
}
再来看ok:
inline bool ok() {
int jw=0;//计算进位
for(int i=n-1;i>=0;i--) {
if((v[a[i]]+v[b[i]]+jw)%n!=v[c[i]])//判断每一位是否成立
return 0;//不成立
jw=(v[a[i]]+v[b[i]]+jw)/n;//继续算进位
}
if(jw)//还有进位没算到,答案肯定有问题
return 0;
return 1;
}
然后是pr:
inline void pr() {
for(int i=65;i<=64+n;i++)就是输出嘛,A对应的ASCLL码值为65
if(v[i]!=-1)//不为-1就输出
printf("%d ",v[i]);
exit(0);//有答案了,直接结束整个程序
}
再来看最复杂的(汗)
check:
inline bool check() {
for(int i=n-1;i>=0;i--)
if(v[a[i]]!=-1&&v[b[i]]!=-1&&v[c[i]]!=-1)//三个都有值
if((v[a[i]]+v[b[i]])%n!=v[c[i]]&&(v[a[i]]+v[b[i]]+1)%n!=v[c[i]])//已经不成立了
return 0;
else if(v[a[i]]!=-1&&v[b[i]]!=-1&&v[c[i]]==-1)//自己看吧,下面三个差不多的
if(flag[(v[a[i]]+v[b[i]])%n]&&flag[(v[a[i]]+v[b[i]]+1)%n])
return 0;
else if(v[a[i]]!=-1&&v[b[i]]==-1&&v[c[i]]!=-1)
if(flag[(v[c[i]]-v[a[i]]+n)%n]&&flag[(v[c[i]]-v[a[i]]-1+n)%n])
return 0;
else if(v[a[i]]==-1&&v[b[i]]!=-1&&v[c[i]]!=-1)
if(flag[(v[c[i]]-v[b[i]]+n)%n]&&flag[(v[c[i]]-v[b[i]]-1+n)%n])
return 0;
return 1;
}
预处理就不用我说了,大家还有不懂的可以一起讨论讨论(^ v ^)