SYSU每周一赛(13.03.16)1002

//写在前面 To二鸣君:鉴于你让我来贴代码,过后要再请我吃一顿饭才行~

题目大意:给出用字母代表数字的加法等式,求出满足等式的字母对应数字的方案数。

思路: 搜索+剪枝。

我的几点优化:

1. 算出每个字母对应的系数,减少搜索时的计算。

2. 计算上下界时很暴力的按照每个字母上界为9,下界为当前搜索的数字(我是按照数字1-9搜的)来算。(感谢二鸣君。)

 代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

char s[13][10],c[10];
int n,m,val[256],score[256],ans;

int calc(int t){ //计算二进制中1的个数
    int ans = 0;
    while (t){
        ans += (t&1);
        t = t>>1;
    }
    return ans;
}

int max(int state,int d){//估计上界
    int ans = 0;
    int max = 9,min = d;
    for (int x = 0;x < m;++x)
        if((state & (1 << x)) == 0){
            if (score[c[x]]>0) ans += score[c[x]]*(max);
            else ans += score[c[x]]*(min);
        }
    return ans;
}

int min(int state,int d){//估计下界
    int ans = 0;
    int max = 9,min = d;
    for (int x = 0;x < m;++x)
        if((state & (1 << x)) == 0){
            if (score[c[x]]>0) ans += score[c[x]]*(min);
            else ans += score[c[x]]*(max);
        }
    return ans;
}

void solve(int d, int state, int sum){//d为当前搜索的数字;state为状态,表示每个字母是否对应了数字;sum为当前的总和

    if (sum + min(state,d)>0 || sum + max(state,d)<0) return;//上下界剪枝

    if(m - calc(state) > 10 - d) return;//未知的数字个数少于未知的字母,剪枝

    if(state == (1 << m) - 1){ //边界
        if(sum == 0) ++ans; //sum==0 符合等式
        return;
    }

    solve(d + 1,state,sum);

    for(int i = 0;i < m;++i){//枚举……
        if((state & (1 << i)) == 0){
            solve(d + 1,state | (1 << i),sum + score[c[i]] * d);
        }
    }
}

int main(){

    bool used[256],cantzero[256];

    while(cin>>n,n){

        memset(used,0,sizeof used);
        memset(cantzero,0,sizeof cantzero);
        memset(score,0,sizeof score);

        for(int i = 0;i < n;++i){
            cin>>s[i];
            int L = strlen(s[i]);
            if(L > 1) cantzero[s[i][0]] = true;

            for(int j = L - 1,pow10 = 1;j >= 0;--j,pow10 *= 10){
                used[s[i][j]] = true;
                score[s[i][j]] += (i == n - 1? -pow10 : pow10);
            }
        }
        m = 0;

        for(int i = 'A';i <= 'Z';++i)
            if(used[i]) c[m++] = i;

        ans = 0;
        solve(1,0,0);
        for(int i = 0;i < m;++i){
            if(!cantzero[c[i]]){
                solve(1,(1 << i),0);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

  

转载于:https://www.cnblogs.com/USTC-ACM/archive/2013/03/19/2969140.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值