题目连接:该题是luogu试炼场的2-7:T6
题目大意:
1 给出一个3*n的字母棋盘,要求用[0,n)这n个数字替换棋盘里的字母,使得棋盘形成一个n进制的合法的竖式。
解题思路:经典的深搜复杂题,NOIP2004的 T4
1 按照位置搜索,N的最大之是26,所以会超时;
2 想剪枝:进位的处理,数字的差重。
3 玄学:枚举的时候从大到小会优化很多时间。
上代码:
//luogu1092: 虫吃算
//棋盘深搜+剪枝
#include<bits/stdc++.h>
using namespace std;
int n;
int a[5][110],b[110],f[110];
char st[110];
void inp()//1:输入与转换 :a1+a2=a3
{
scanf("%d",&n);
for(int i=1;i<=3;i++)
{
scanf("%s",st+1);
for(int j=1;j<=n;j++) a[i][j]=st[j]-'A'+1;
}
}
bool pd2()//过程判断:1:最高位;2:各数位进位情况
{
if(f[a[1][1]]+f[a[2][1]]>=n) return 0;//最高位不可进位
for(int i=n;i>=1;i--)
{
int x=f[a[1][i]],y=f[a[2][i]],z=f[a[3][i]];
if(x==-1||y==-1||z==-1) continue;//未处理到
if((x+y)%n!=z&&(x+y+1)%n!=z) return 0;//无法匹配
}
return 1;
}
bool ch()//是否有数字没用完
{
for(int i=1;i<=n;i++) if(f[i]==-1) return 0;
return 1;
}
bool pd()//最终判断:式子是否合法
{
int k=0;//进位的值
for(int i=n;i>=1;i--)
{
int x=f[a[1][i]],y=f[a[2][i]],z=f[a[3][i]];
if((x+y+k)%n!=z) return 0;//不合法
k=(x+y+k)/n;//下次的进位
}
return 1;
}
void dfs(int x,int y,int t)//x行y列,进位是t
{
if(ch()||y==0)//判断是否全部数字都用完了或者全盘扫描了:
{
if(pd()) //式子完全合法
{
for(int i=1;i<=n;i++) printf("%d ",f[i]);
exit(0);//终结
}
return ;
}
if(pd2()==0) return ;//剪枝:过程进位判断
//以上是判断与剪枝
//===============================================
//以下是每一次的执行
if(f[a[x][y]]==-1)//当前位,没被赋值
{
for(int i=n-1;i>=0;i--)
{
if(b[i]==0)//i没被用过,可用:
{
if(x<3)//一二行
{
f[a[x][y]]=i;
b[i]=1;
dfs(x+1,y,t);//同列,下一行
b[i]=0;
f[a[x][y]]=-1;
}
else if(x==3)//第三行
{
int z=f[a[1][y]]+f[a[2][y]]+t;
if(z%n!=i) continue;
f[a[x][y]]=i;
b[i]=1;
dfs(1,y-1,z/n);//向左一位
b[i]=0;
f[a[x][y]]=-1;
}
}
}
}
else//当前位已取值
{
if(x<3) dfs(x+1,y,t);
else//第三行
{
int z=f[a[1][y]]+f[a[2][y]]+t;
dfs(1,y-1,z/n); //左移一位
}
}
}
int main()
{
freopen("testdata.in","r",stdin);
inp();//输入与整理
memset(f,-1,sizeof(f));//i字符表示的f[i]的值
memset(b,0,sizeof(b));//i数字是否用过
dfs(1,n,0);//从第1行第n列开始,进位是 0
return 0;
}