digit puzzle 数字谜 Uva12107

来源:互联网 发布:淘宝网上开店需要多少钱 编辑:程序博客网 时间:2024/04/28 11:34

题目

digit puzzle 数字谜 Uva12107
给一个不完整表达式,要求修改尽量少的数,使修改后的数字谜只有唯一解。空格和数字可以随意替换,但不能增删,表达式中所有涉及的数必须是没有前导零的正数。输入数字谜一定形如a*b=c,其中a、b、c分别最多有2、2、4位。用下划线表示未知数字。输入保证会有解,即有经过变换后肯定能有一个表达式有唯一解。如果有多个解,输出字典序最小的解, 下划线比数字小

 输入      _*__=78 //表达式 答案不唯一 1*78=78 2*39=78 输出     _*_7=_8 //表达式唯一解 4*17=68; 右原始表达式第3个下划线和7交换1次得到tip:    7*__=_8 //表达式也有唯一解 7*14=98; 但修改次数也是1 但字典序不是最小

思路

初始状态Swhile True:    每次交换一个不同的数字或下划线 形成新的表达式S    if 表达式S 有唯一解:        停止搜索,结束

1、首先要有一个逻辑 判断表达式解是否唯一
2、每次交换一个不同的数字或下划线 形成新的表达式,校验是否唯一解,不唯一重复步骤2

问题

1、如何保证修改次数最小?
IDA*深度迭代搜索
2、如何保证修改次数相同时 字典序最小?
在搜索时保证首先搜索字典序最小的状态,那么第一个搜索到的解就是答案
3、如何判断表达式解是否唯一?

初始状态Sall=0def isOk(S):    if S 中没有空格:        if 校验表达式==成立:            all++;    else for 下划线 in 所有的下换线:        替换下划线 = 0,1,2,...,9 获得新的状态S1。        递归S1#上述思路 可以优化,不过下面代码没有实现# 1\ 替换a、b中的下换线。计算c=a*b, 匹配c与表达式中c # 2\ 一旦all >=2后 就不需要查找了。肯定不是唯一解

4、如何存储表达式 (这是任何题目都避免不了的问题)
因为最多涉及8个数字或下划线 ,用4个bit存储一个数字, 正好用1个int型=32位;
因为下划线比数字小,所以空格用0=0000表示 数字用原始数字+1存储即[0001, 1010] 不存在用NAN=15=1111表示。如果数字是无符号的int型,那么数字越小对应的字典序也越小

案例验证

输入 _*__=78输出 _*_7=_8

代码

/* * 思路 * 最多涉及8个数字 用4个bit存储一个数字, 正好32位=1个int型; 空格用0=0000表示 数字用原始数字+1存储即[0001, 1010] 不存在用NAN=15=1111表示,一定是数无符号整型 * 初始状态s0 * 转移方程 s = { sj | si中8个数字或空格, 除15外 任意两个不同的值交换得到sj } * */#define MAXDEPTH 10#define NAN 15// 交换s[i]<-->s[j]#define chg(s, i, j)\do{\    int __i, __j, __k;\    for( __i=(i)*4, __j=(j)*4, __k=0; __k<4; __k++, __i++, __j++)\    {\        if ( (s&1<<__i)>>__i != (s&1<<__j)>>__j )\        {\            if ( s&1<<__i ) { s &= ~(1<<__i);  s |= (1<<__j); }\            else     { s &= ~(1<<__j);  s |= (1<<__i); }\        }\    }\}while(0)//获取s[i]的数字#define slice(s, i) (s>>(i)*4&15)//更新s[i]的数字=v#define update(s, i, v) do{ s &= ~(15<<(i)*4); s |= (v)<<(i)*4; } while(0)int MaxDepth;int depth;int ans;static int okNum(int s, int index){    int i, k, v, a, b, c, num;    num = 0;    // 所有空格替换完毕    if (index==-1)    {        // 解析a,b,c        a=b=c=0;        for(i=0; i<8; i++)        {            // 跳过NAN            if(  slice(s,i) == NAN ) continue;            if(i==0) {                c += (slice(s,i)-1);            }            else if(i==1) {                c += (slice(s,i)-1) * 10;            }            else if(i==2) {                c += (slice(s,i)-1) * 100;            }            else if(i==3) {                c += (slice(s,i)-1) * 1000;            }            else if(i==4) {                b += (slice(s,i)-1);            }            else if(i==5) {                b += (slice(s,i)-1) * 10;            }            else if(i==6) {                a += (slice(s,i)-1);            }            else if(i==7) {                a += (slice(s,i)-1) * 10;            }        }        return a*b==c ? 1:0 ;    }    /*    * 遇到空格0 时填充数字1,2,...,10, 保证没有前导0    * 递归到index-1    */    if(  slice(s,index) == 0 )    {        for(v=0; v<10; v++)        {            // 1、index=3 5 7时 不能为0.            // 2、前一个index+1=1(值为0)或NAN(不存在)时 不能为0            if(v==0)            {                if( index==3 || index==5 || index==7) continue;                if( slice(s,index+1)==1 || slice(s,index+1)==NAN) continue;            }            update(s, index, v+1);            num += okNum(s, index-1);        }    }    else    {        num += okNum(s, index-1);    }    return num;}/* 搜索第一个符合条件的答案 */static int search(int s){    int i,j, g, v, a, b, k;    unsigned int tmp, sNew[28]; // 数量<=8时 任意交换两个最多7*8/2=28种可能    if (depth >= MaxDepth ) return 0;    if( okNum(s, 7)==1 )    {        ans = s;        return 1;    }    // 初始化sNew 在有序集合{s|交换s中任意两个不相同的数字或空格, si<sj }    g=-1;    for(i=0; i<8; i++) if( slice(s, i)!=NAN )        {            for(j=i+1; j<8; j++) if( slice(s, j)!=NAN )                {                    if(slice(s, i)==slice(s, j)) continue;                    tmp = s;                    chg(tmp, i, j);                    k=g;                    while(k>=0 && sNew[k] > tmp) {                        sNew[k+1]=sNew[k];                        k--;                    }                    sNew[k+1]=tmp;                    g++;                }        }    // 寻找需要交换的a, b 使得交换后 中最小k小    // 按照交换后字典序 曾序递归    for(i=0; i<=g; i++)    {        depth++;        if ( search(sNew[i]) ) return 1;        depth--;    }    return 0;}int main(){    int i,j,k,l, n, s0;    char text[8+1];    scanf("%s", text);    //初始化答案    ans=-1;    // 初始化s0    l=-1;    while(text[++l]!='\0');    s0=-1;    k=0;    for(i=0,j=l-1; j>=0; j--)    {        if( text[j] == '=' ) i = 4;        else if( text[j] == '*') i = 6;        else        {            update(s0, i, (text[j]=='_'?0:(text[j]-'0')+1));            i++;        }    }    // printf("s0:");    // ppp(&s0, 32);    // IDA*搜索    for(MaxDepth=1; MaxDepth<MAXDEPTH;  MaxDepth++)        if( search(s0) ) break;    // 打印答案    // printf("ans:");    // ppp(&s0, 32);    if( ans==-1 ) printf("no solution\n");    else    {        for(i=7; i>=0; i--)        {            if(i==5) printf("*");            if(i==3) printf("=");            j = slice(ans, i);            if(j==0) printf("_");            else if(j!=NAN) printf("%d", j-1);        }        printf("\n");    }    return 0;}