1. 程式人生 > >蟲食算題解

蟲食算題解

蟲食算題目出處(3703)

此題的直接思路是搜尋,但搜尋得有技術(其實就是剪枝),不然會超時
膜拜自己
這道題從個位往最高位搜,就可以避免進位的重複運算。。。。。。
最後check一下,然後遞迴輸出
先上程式碼,後面再來分段討論

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
bool flag[95];
char a[30],b[30],c[30],d[30];
int v[95],n;
inline
void ycl() { int tot=0; for(int i=n-1;i>=0;i--) { if(!flag[a[i]]) { flag[a[i]]=1; d[++tot]=a[i]; } if(!flag[b[i]]) { flag[b[i]]=1; d[++tot]=b[i]; } if(!flag[c[i]]) { flag[c[i]]=1; d[
++tot]=c[i]; } } memset(flag,0,sizeof(flag)); memset(v,-1,sizeof(v)); } inline bool ok() { int jw=0; for(int i=n-1;i>=0;i--) { if((v[a[i]]+v[b[i]]+jw)%n!=v[c[i]]) return 0; jw=(v[a[i]]+v[b[i]]+jw)/n; } if(jw) return 0; return 1
; } inline void pr() { for(int i=65;i<=64+n;i++) if(v[i]!=-1) printf("%d ",v[i]); exit(0); } inline bool check() { for(int i=n-1;i>=0;i--) if(v[a[i]]!=-1&&v[b[i]]!=-1&&v[c[i]]!=-1) if((v[a[i]]+v[b[i]])%n!=v[c[i]]&&(v[a[i]]+v[b[i]]+1)%n!=v[c[i]]) return 0; else if(v[a[i]]!=-1&&v[b[i]]!=-1&&v[c[i]]==-1) if(flag[(v[a[i]]+v[b[i]])%n]&&flag[(v[a[i]]+v[b[i]]+1)%n]) return 0; else if(v[a[i]]!=-1&&v[b[i]]==-1&&v[c[i]]!=-1) if(flag[(v[c[i]]-v[a[i]]+n)%n]&&flag[(v[c[i]]-v[a[i]]-1+n)%n]) return 0; else if(v[a[i]]==-1&&v[b[i]]!=-1&&v[c[i]]!=-1) if(flag[(v[c[i]]-v[b[i]]+n)%n]&&flag[(v[c[i]]-v[b[i]]-1+n)%n]) return 0; return 1; } inline void dfs(int i) { if(i>n) { if(ok()) pr(); return; } for(int j=n-1;j>=0;j--) { if(flag[j]) continue; v[d[i]]=j; flag[j]=1; if(!check()) { v[d[i]]=-1; flag[j]=0; continue; } dfs(i+1); v[d[i]]=-1; flag[j]=0; } } int main() { scanf("%d\n",&n); gets(a); gets(b); gets(c); ycl(); dfs(1); }

主函式的程式碼很好懂,我們就直接看dfs:

inline void dfs(int i) {
    if(i>n) {
        if(ok())
            pr();
        return;
    }
    for(int j=n-1;j>=0;j--) {
        if(flag[j])
            continue;
        v[d[i]]=j;
        flag[j]=1;
        if(!check()) {
            v[d[i]]=-1;
            flag[j]=0;
            continue;
        }
        dfs(i+1);
        v[d[i]]=-1;
        flag[j]=0;
    }
}

再來看ok:

inline bool ok() {
    int jw=0;//計算進位
    for(int i=n-1;i>=0;i--) {
        if((v[a[i]]+v[b[i]]+jw)%n!=v[c[i]])//判斷每一位是否成立
            return 0;//不成立
        jw=(v[a[i]]+v[b[i]]+jw)/n;//繼續算進位
    }
    if(jw)//還有進位沒算到,答案肯定有問題
        return 0;
    return 1;
}

然後是pr:


inline void pr() {
    for(int i=65;i<=64+n;i++)就是輸出嘛,A對應的ASCLL碼值為65
        if(v[i]!=-1)//不為-1就輸出
            printf("%d ",v[i]);
    exit(0);//有答案了,直接結束整個程式
}

再來看最複雜的(汗)

check:

inline bool check() {
    for(int i=n-1;i>=0;i--)
        if(v[a[i]]!=-1&&v[b[i]]!=-1&&v[c[i]]!=-1)//三個都有值
            if((v[a[i]]+v[b[i]])%n!=v[c[i]]&&(v[a[i]]+v[b[i]]+1)%n!=v[c[i]])//已經不成立了
                return 0;
        else if(v[a[i]]!=-1&&v[b[i]]!=-1&&v[c[i]]==-1)//自己看吧,下面三個差不多的
            if(flag[(v[a[i]]+v[b[i]])%n]&&flag[(v[a[i]]+v[b[i]]+1)%n])
                return 0;
        else if(v[a[i]]!=-1&&v[b[i]]==-1&&v[c[i]]!=-1)
            if(flag[(v[c[i]]-v[a[i]]+n)%n]&&flag[(v[c[i]]-v[a[i]]-1+n)%n])
                return 0;
        else if(v[a[i]]==-1&&v[b[i]]!=-1&&v[c[i]]!=-1)
            if(flag[(v[c[i]]-v[b[i]]+n)%n]&&flag[(v[c[i]]-v[b[i]]-1+n)%n])
                return 0;
    return 1;
}

預處理就不用我說了,大家還有不懂的可以一起討論討論(^ v ^)