[NOIP2004提高组]虫食算(搜索剪枝)

30pts:善用next_permutation

50pts:先枚举每一位是否有进位,设进位为di=0/1,然后a[i]+b[i]+d[i-1]-10d[i]=c[i],移项后为a[i]+b[i]-c[i]=10d[i]-d[i-1],n位形成n个方程组,可以进行高斯消元,时间复杂度O(2^n*n^3)

100pts:高斯消元是一个死胡同,但似乎也有这种方法做出来的,但我太菜了学不会。

改用搜索剪枝。实际上很多状态搜了一点点就会发现它是错的。于是从低位向高位枚举字母,每次枚举后就判断是否可行,不可行就不再往下搜索,这样可以搜过去。

#include<bits/stdc++.h>
using namespace std;
int n,cnt,s[300],used[30],p[30];
char a[30],b[30],c[30];
bool check()
{
    for(int i=n;i;i--)
    if(s[a[i]]!=-1&&s[b[i]]!=-1&&s[c[i]]!=-1&&(s[a[i]]+s[b[i]])%n!=s[c[i]]&&(s[a[i]]+s[b[i]]+1)%n!=s[c[i]])
    return 0;
    return 1;
}
void work()
{
    int jw=0,sum;
    for(int i=n;i;i--)
    {
        sum=s[a[i]]+s[b[i]]+jw;
        if(s[c[i]]!=sum%n)return;
        jw=sum/n;
    }
    for(int i='A';i<'A'+n;i++)printf("%d ",s[i]);
    exit(0);
}
void dfs(int u)
{
    if(u>n){work();return;}
    for(int i=n-1;~i;i--)
    if(!used[i])
    {
        s[p[u]+'A'-1]=i;
        if(check()){used[i]=1;dfs(u+1);used[i]=0;}
    }
    s[p[u]+'A'-1]=-1;
}
int main()
{
    scanf("%d",&n);
    scanf("%s%s%s",a+1,b+1,c+1);
    memset(s,-1,sizeof s);
    for(int i=n;i;i--)
    {
        if(!used[a[i]-'A'+1])p[++cnt]=a[i]-'A'+1,used[a[i]-'A'+1]=1;
        if(!used[b[i]-'A'+1])p[++cnt]=b[i]-'A'+1,used[b[i]-'A'+1]=1;
        if(!used[c[i]-'A'+1])p[++cnt]=c[i]-'A'+1,used[c[i]-'A'+1]=1;
    }
    memset(used,0,sizeof used);
    dfs(1);
}

 

posted @ 2021-01-27 18:46  hfctf0210  阅读(117)  评论(0编辑  收藏  举报