NOIP2004 虫食算

题面https://www.luogu.org/problemnew/show/P1092

肯定要从右到左,从上到下比较好处理。

必然记录一个数用过与否,某个字母是什么。

剪枝:

1.到了第三行,上面两个已经确定,可以判断。

2.往后预估。但是进位怎么考虑??

没有关系!进位最多一位!!

如果对于已经填好2个的3元组,可以推出另外一个的2种可能情况,如果都被占据,return false

对于3个都填好了。也可以这样判断。

(官方正解是高斯消元,但是我认为复杂度不对,因为数据水,好像没有21以上的n)

代码:

(数据的原因,倒序枚举更快)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=28;
char mp[4][N];
int is[N];
bool has[N];
int ans[N];
int n;
int num[4][N];
bool fl;
int lp(int x,int y){
    return mp[x][y]-'A';
}
bool che(int st){
    //return true;
    int jin=0;//return true;
    for(int j=st;j>=1;j--){
        int a=is[lp(1,j)],b=is[lp(2,j)],c=is[lp(3,j)];
        if(a!=-1&&b!=-1&&c!=-1){
            if(c!=(a+b)%n&&c!=(a+b+1)%n) return false;
        }
        else if(a!=-1&&b!=-1&&c==-1){
            if(has[(a+b)%n]&&has[(a+b+1)%n]) return false;
        }
        else if(a!=-1&&b==-1&&c!=-1){
            if(has[((c-a-1)+n)%n]&&has[((c-a)+n)%n]) return false;
        }
        else if(a==-1&&b!=-1&&c!=-1){
            if(has[((c-b-1)+n)%n]&&has[((c-b)+n)%n]) return false;
        }
    }
    return true;
}
void dfs(int x,int y,int jin){
    //cout<<x<<" and "<<y<<endl;
    if(fl) return;
    if(x==1&&y==0){
        if(jin) return;
        fl=true;
        memcpy(ans,is,sizeof is);return;
    }
    if(!che(y)) {
    return;
    }
    if(x==3){
        int a=is[mp[1][y]-'A'];
        int b=is[mp[2][y]-'A'];
        int tmp=a+b+jin;
        int to=tmp%n;
        if(is[mp[x][y]-'A']==-1){
            if(has[to]) return;
            has[to]=1;
            is[mp[x][y]-'A']=to;
            num[x][y]=to;
            dfs(1,y-1,tmp/n);
            has[to]=0;
            is[mp[x][y]-'A']=-1;
            num[x][y]=-1;
        }
        else{
            if(is[lp(x,y)]!=to) return;
            dfs(1,y-1,tmp/n);
        }
    }
    else{
        if(is[mp[x][y]-'A']==-1)
            for(int i=n-1;i>=0;i--){
                if(!has[i]){
                    has[i]=1;
                    is[mp[x][y]-'A']=i;
                    num[x][y]=i;
                    dfs(x+1,y,jin);
                    has[i]=0;
                    is[mp[x][y]-'A']=-1;
                    num[x][y]=-1;
                }
            }
        else {
            num[x][y]=is[lp(x,y)];
            dfs(x+1,y,jin);
            num[x][y]=-1;
        }
    }
}
int main(){
    scanf("%d",&n);
    fl=false;
    for(int i=1;i<=3;i++)scanf("%s",mp[i]+1);
    memset(is,-1,sizeof is);
    memset(num,-1,sizeof num);
    dfs(1,n,0);
    //cout<<fl<<endl;
    for(int i=0;i<n;i++){
        printf("%d ",ans[i]);
    }return 0;
}

总结:
往后预估不一定要很准确。

毕竟剪枝是玄学的。

可以稍微放宽一些,反而更加有效。

原文地址:https://www.cnblogs.com/Miracevin/p/9779571.html