[NOIP2004][CODEVS1064]虫食算(搜索||高斯消元)

题目描述

传送门

题解

一看这就是爆搜题嘛,据说有人说高斯消元才是正解戳这里
但是这个爆搜的技巧还是很多的,让我学到了有用的东西:
①每搜出来一个数都要判断是否合法。这样看起来每次都要 O(n) 扫一遍,但是效果却非常好,可以剪掉大量的废枝。对于判断,我刚开始只考虑了没有进位的部分,有进位的部分不进行判断,这样的话实际上非常没用。那么如果要考虑到进位的问题,A+B=C的话,如果(A+B)%n!=C且(A+B+1)%n!=C,那么ABC一定不是合法解。并且如果知道了ABC其中的两个,那么可以算出第三个,并且也要分进位和不进位两种情况,如果两种情况算出来的数之前都用过,那么这组解也不合法。
②判断合法的方式提醒我们,对于每一项,ABC知道得越多越容易被剪枝。那么可以改变搜索的顺序,按照字母从右往左出现的顺序搜索。这个效果也非常明显。
③填数的时候倒序搜比正序搜要快很多。why?

代码

#include
#include
#include
#include
using namespace std;

char a[30],b[30],c[30],s[100];
int n,len,ans[30];
bool vis[30];

bool check()
{
    int last=0,aa,bb,cc;
    for (int i=n-1;i>=0;--i)
    {
        aa=ans[a[i]-'A'+1],bb=ans[b[i]-'A'+1],cc=ans[c[i]-'A'+1];
        if (aa==-1||bb==-1||cc==-1) {last=-1;continue;}
        if (last==-1)
        {
            if ((aa+bb)%n!=cc&&(aa+bb+1)%n!=cc) return false;
        }
        else
        {
            if ((aa+bb+last)%n!=cc) return false;
            last=(aa+bb+last)/n;
        }   
    }
    if (last!=-1&&last!=0) return false;
    return true;
}
void dfs(int dep)
{
    if (dep==len+1)
    {
        if (check())
        {
            for (int i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]);
            exit(0);
        }
        return;
    }
    int now=s[dep]-'A'+1;
    if (ans[now]!=-1) dfs(dep+1);
    else
        for (int i=n-1;i>=0;--i)
            if (!vis[i])
            {
                ans[now]=i;
                vis[i]=true;
                if (check())
                    dfs(dep+1);
                ans[now]=-1;
                vis[i]=false;
            }
}
int main()
{
    scanf("%d\n",&n);
    gets(a); gets(b); gets(c);
    for (int i=n-1;i>=0;--i)
    {
        if (!vis[a[i]-'A'+1]) vis[a[i]-'A'+1]=true,s[++len]=a[i];
        if (!vis[b[i]-'A'+1]) vis[b[i]-'A'+1]=true,s[++len]=b[i];
        if (!vis[c[i]-'A'+1]) vis[c[i]-'A'+1]=true,s[++len]=c[i];
    }
    memset(ans,-1,sizeof(ans)); memset(vis,0,sizeof(vis));
    dfs(1);
}

总结

①分析性质!分析性质!分析性质!非常重要!!!

你可能感兴趣的:(题解,搜索,NOIP,高斯消元)