24点游戏(A better solution)

接上一篇文章,这篇文章将介绍24点游戏的一种改进的编程方法,将减少枚举过程的冗余。思路是编程之美上的解法二,思想是集合和动态规划(set & DP),利用了子集的结果求出更大集合的解,最后求出整个集合上面的的所有表达式,再进行筛选。书上的文字解释写的不太清楚,需要结合伪代码看,所以在此就把书上的伪代码拷贝过来,再附上自己具体实现的C++代码,欢迎大家指正!

伪代码如下:

24Game(Array)        // Array为初始输入的集合,其中元素表示为ai(0<=i<=n-1)
{
     for(int i = 1; i < = 2n - 1; i++)
          S[i] =  ;    // 初始化将S中各个集合置为空集,n为集合Array的元素个数,
                        // 在24点中即为4,后面出现的n具相同含义
     for(int i = 0; i < n; i++)
          S[2i] = {ai};      // 先对每个只有一个元素的真子集赋值,即为该元素本身
     for(int i = 1; i < = 2n - 1; i++)   // 每个i都代表着Array的一个真子集
          S[i] = f(i);
     Check(S[2n - 1]);             // 检查S[2n-1]中是否有值为24的元素,并返回 
}
 
f(int i)          // i的二进制表示可代表集合的一个真子集,具体含义见上面的分析
{
     if(S[i] )
          return S[i];
     for(int x = 1; x < i; i++)    // 只有小于i的x才可能成为i的真子集
          if(x & i == x)// &为与运算,只有当x&i==x成立时x才为i的子集,此时i-x为i的
                        // 另一个真子集,x与i-x共同构成i的一个划分,读者可自行验证
               S[i] = Fork(f(x), f(i-x));  //  为集合的并运算,Fork见
                                               // 定义1-16-1,在Fork的过程中,
                                               // 去除重复中间结果……
}

 实现的C++代码如下:

//24points BOP(2)
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
using namespace std;

const int MaxN=1000;
const int CardN=4;
const double eps=1e-6; 

typedef struct ans* link; 
struct ans
{
       double data;
       string  str;   
};
 
int num[MaxN];
vector<ans> result[MaxN];
vector<string> ss; 

void check(vector<ans> x)
{
     for(int i=0; i<=x.size(); i++)
     {
             if(fabs(x[i].data - 24) < eps) ss.push_back(x[i].str); 
     }     
}

vector<ans> joint(vector<ans> x, vector<ans> y)
{
            vector<ans> z;
            z.clear(); 
            for(int i=0; i<x.size();i++)
            {
                    for(int j=0; j<y.size(); j++)
                    {
                            link temp=new ans;
                            (*temp).data=x[i].data+y[j].data;
                            (*temp).str='('+x[i].str+'+'+y[j].str+')';
                            z.push_back(*temp);
                            
                            link temp1=new ans;
                            (*temp1).data=x[i].data-y[j].data;
                            (*temp1).str='('+x[i].str+'-'+y[j].str+')';
                            z.push_back(*temp1);
                            
                            link temp2=new ans;
                            (*temp2).data=y[j].data-x[i].data;
                            (*temp2).str='('+y[j].str+'-'+x[i].str+')';
                            z.push_back(*temp2);
                            
                            link temp3=new ans;
                            (*temp3).data=x[i].data*y[j].data;
                            (*temp3).str='('+x[i].str+'*'+y[j].str+')';
                            z.push_back(*temp3);
                            
                            if(y[j].data > eps)
                            { 
                            link temp4=new ans;
                            (*temp4).data=x[i].data/y[j].data;
                            (*temp4).str='('+x[i].str+'/'+y[j].str+')';
                            z.push_back(*temp4);
                            }
                            
                            if(x[i].data > eps)
                            { 
                            link temp5=new ans;
                            (*temp5).data=y[j].data/x[i].data;
                            (*temp5).str='('+y[j].str+'/'+x[i].str+')';
                            z.push_back(*temp5);
                            }       
                    } 
            }
            return z;           
} 

vector<ans> f(int i)
{
            vector<ans> res;
            res.clear(); 
            if(!result[i].empty()) return result[i];
            for(int j=1; j<i; j++)
            {
                    if((j&i) == j)
                             {
                                  vector<ans> tmp; 
                                  tmp.clear();
                                  tmp=joint(f(j), f(i-j));
                                  res.insert(res.end(), tmp.begin(), tmp.end()); 
                             } 
            }
            return res;     
} 

void point()
{
     for(int i=1; i<=((1<<CardN)-1); i++) result[i].clear();
     //initialize the sets with one element;
     for(int i=1; i<=CardN; i++) 
     {
             char buffer[MaxN];
             itoa(num[i], buffer, 10);
             link temp=new ans;
             (*temp).data=num[i];
             (*temp).str=buffer; 
             result[1<<(i-1)].push_back(*temp);
     } 
     for(int i=1; i<=((1<<CardN)-1); i++) result[i]=f(i);
     check(result[(1<<CardN)-1]);   
}

int main()
{
    ss.clear(); 
    int x;
    for(int i=1; i<=4; i++)
    {
            cin>>x;
            num[i]=x;      
    }
    point();
    sort(ss.begin(),ss.end());
    vector<string>::iterator it=unique(ss.begin(), ss.end());
    for(vector<string>::iterator it1=ss.begin(); it1!=it; it1++) cout<<*it1<<endl; 
    system("pause");
    return 0; 
} 

测试结果和上一篇文章中的两个naive solutions的结果是一样的,如下:

测试用例1:
Input:

5 5 5 1

Output:

(5-(1/5))*5

5*(5-(1/5))


测试用例2:

Input:

3 3 7 7

Output:

((3/7)+3)*7

(3+(3/7))*7

7*((3/7)+3)

7*(3+(3/7))


测试用例3:

Input:

3 3 8 8

Output:

8/(3-(8/3))

 

To be continued...


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值