蟲食算題解
阿新 • • 發佈:2018-12-27
蟲食算題目出處(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 ^)