NOIP2004虫食算

这个题官方正解是高斯消元,可是我不会啊QAQ。
  • 说一下搜索怎么做

  • 这个题目第一个难点在于你要理解 n 进制的加法

  • n 进制的加法就是在十进制的基础上满十进一改成满 n 进一

  • 由于这道题只考虑加法,所以进位只可能是 1 ,证明小学生都会,略

  • 搜索的大体思路就是从第 1 位的值开始搜,搜到最后一位,判断是否合法

  • 考虑剪枝

  • 3 个字符串的长度都是 n ,由此可以想到一个最简单的剪枝

  • 最高位不能有进位

  • 如果有进位,显然第 3 个串的长度不会是 n ,而是n+1,这并不合法

  • 一个剪枝显然不够啊,再想一个

  • 文字不太好描述,我们看图(不会用latex写竖式啊QAQ)

    qwq

  • 假设这是十进制下的加法,怎么判断这个竖式对不对?

  • 显然这个竖式是错误的,因为个位上 (8+6)mod10=45

  • 由此推广到每一位,但是还要考虑进位,不慌,看另一张图

    qwq

  • 这个竖式是对的还是错的?

  • 这并不好判断,虽然 (8+6)mod10=45 ,但是这是中间位,有可能有进位

  • 如果有进位, 那么 (8+6+1)mod10=5 ,这一位就是合法的了。

  • 综合上面的分析,得到了另一个剪枝方法

  • A B 表示两个加数,用 C 表示两个加数的和

  • 如果某 i 位,满足 (A[i]+B[i])modnC[i] (A[i]+B[i]+1)modnC[i]

  • 根据上面的分析,这种状态肯定不对,直接 return 就好了

  • 或许还有别的剪枝,但是这两个应该够用了

  • 我的代码里还用了一个玄学的 next 数组,有什么用照着样例手推一遍就知道了,比较好理解。实在看不懂可以私信我qwq

学习科学,使用玄学——某钟姓dalao

#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 30
int a[maxn],b[maxn],c[maxn];
int num[maxn],Next[maxn],n,cnt;
char s1[maxn],s2[maxn],s3[maxn];
bool used[maxn];
bool Judge() {
    for(int i=n-1,x=0;i>=0;i--) {
        int A=num[a[i]],B=num[b[i]],C=num[c[i]];
        if((A+B+x)%n!=C) return false;
        x=(A+B+x)/n;
    }
    return true;
}
bool CanPrune() {//prune: 剪枝—百度翻译。
    if(num[a[0]]+num[b[0]]>=n)
        return true;
    for(int i=n-1;i>=0;i--) {
        int A=num[a[i]],B=num[b[i]],C=num[c[i]];
        if(A==-1||B==-1||C==-1) continue;
        if((A+B)%n!=C&&(A+B+1)%n!=C)
            return true;
    }
    return false;
}
void Print() {
    for(int i=0;i<n;i++)
        printf("%d ",num[i]);
    exit(0);
}
void dfs(int x) {
    if(CanPrune()==true) return;
    if(x==n) {
        if(Judge()==true) Print();
        return;
    }
    for(int i=n-1;i>=0;i--)
        if(used[i]==false) {
            num[Next[x]]=i;
            used[i]=true;
            dfs(x+1);
            num[Next[x]]=-1;
            used[i]=false;
        }
    return;
}
inline int id(char c) {
    return c-'A';
}
void GetNext(int x) {
    if(used[x]==false) {
        used[x]=true;
        Next[cnt++]=x;
    }
    return;
}
int main() {
    scanf("%d",&n);
    scanf("%s%s%s",s1,s2,s3);
    for(int i=0;i<n;i++) {
        a[i]=id(s1[i]);
        b[i]=id(s2[i]);
        c[i]=id(s3[i]);
        num[i]=-1;
    }
    for(int i=n-1;i>=0;i--) {
        GetNext(a[i]);
        GetNext(b[i]);
        GetNext(c[i]);
    }
    for(int i=0;i<n;i++) used[i]=false;
    dfs(0);
    return 0;
}

代码看不懂也可以问我qwq

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值