首页 > 代码库 > luogu1092 虫食算

luogu1092 虫食算

题目链接:https://www.luogu.org/problem/show?pid=1092

解:

我真的交了好多遍。。

开始,用getchar()一个个字母读入,0分。。。

改成scanf(),70,并且在70的道路上一发不可收拾。。。

后来看了别人的代码才发现,原来我们的枚举是要进行优化的。。。

解:

70分做法:先枚举每个字母代表的数字,然后枚举一个,判断一次,如果一列上的三个字母都有代表的数字了,就判断合不合法,枚举所有的列。最后枚举完后检查一遍。

100分做法:在上一个做法的情况下我们不要从‘A’~‘Z’来一个个枚举代表的数字,而是按照输入的顺序来,我们开一个数组,一列列扫过三个字符串,如果这个字符没在数组里,就把它加进去。最后按照数组中的字母的顺序来进行枚举。很显然按这个枚举方式,更容易在判断的时候时间较短地找到错误,因为两个都是一列列扫的。至于从前往后还是从后往前,就需要与判断时候扫的顺序一致。

程序:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
using namespace std;
int n,num[30],a[30],b[30],c[30],cnt,po[30];
char a1[30],b1[30],c1[30];
bool vis[30],bl[100];
void write()
{
  for (int i=0;i<=n-2;i++)
  printf("%d ",num[i]);
  if (n!=0) printf("%d\n",num[n-1]);
  exit(0);
}
bool check()
{
  for (int i=n;i>=1;i--)
  if ((num[a[i]]!=-1)&&(num[b[i]]!=-1)&&(num[c[i]]!=-1)
  &&(((num[a[i]]+num[b[i]])%n)!=num[c[i]])&&(((num[a[i]]+num[b[i]]+1)%n)!=num[c[i]])) return false;
  return true;
}
bool check2()
{
  int ef=0;
  for (int i=n;i>=1;i--)
  {
      int l=num[a[i]]+num[b[i]]+ef;
      if ((l%n)!=num[c[i]]) return false;
      ef=l/n;
  }
  return true;
}
void dfs(int now)
{
  if ((num[a[1]]+num[b[1]])>n-1) return;
  if (!check()) return; 
  if (now==n) {if (check2()) write();}
  for (int j=n-1;j>=0;j--)
   if (!vis[j])
   {
        num[po[now]]=j;
        vis[j]=true;
     dfs(now+1);
        vis[j]=false;
        num[po[now]]=-1;
   }
}
int main()
{
  scanf("%d",&n);
  scanf("%s%s%s",a1,b1,c1);
  char ch;
  for (int i=0;i<=n;i++) num[i]=-1;
  for (int i=1;i<=n;i++)
  {
      a[i]=a1[i-1]-A;
      b[i]=b1[i-1]-A;
      c[i]=c1[i-1]-A;
  }
  for (int i=n-1;i>=0;i--)
  {  
   if (!bl[a1[i]]) {po[cnt++]=a[i+1];bl[a1[i]]=true;}
   if (!bl[b1[i]]) {po[cnt++]=b[i+1];bl[b1[i]]=true;}
   if (!bl[c1[i]]) {po[cnt++]=c[i+1];bl[c1[i]]=true;}
   if (cnt==n) break;
  }
  dfs(0);
  return 0;
}

 

luogu1092 虫食算