24点

24点

描述

给出四个正整数数,问能否使用加减乘除计算得到24,如果能,给出计算步骤。

例题:luogu1236

对于四个数,运算后得到24一个数,说明运算中用到的运算符个数是三个。这三个运算符可以任选,那么可以直接枚举;现在问题就是已知四个运算数和三个运算符,如何运算可以得到24。

一种想法

直接全排列四个数字,然后将运算符放在相邻两数之间,最后计算整个表达式。

代码

这里用了两个栈存放操作数和操作符,每次弹出两个数一个符运算,将结果压回操作数栈中,直到只剩一个数。

另外生成全排列用了STL库中<algorithm>头文件中的next_permutation()头文件。当然,生成下一个排列的实现,就是另一个故事了。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
template<typename T,int size>
struct STACK
{
	T ary[size];
	int f;
	STACK(){f=0;}
	void init(){f=0;}
	void push(T& w){ary[++f]=w;}
	T pop(){return ary[f--];}
};
char ope[5]={'0','+','-','*','/'};
inline int compute(int x,int y,int op);

int num[5];//for given numbers
int op[5];// for operators;

int ansnum[5];
int ansop[5];
void input()
{
	for(int i=1;i<=4;++i)
    {
        scanf("%d",&num[i]);
    }
}
//char ope[5]={'0','+','-','*','/'};
const int Plus=1,Minus=2,Mul=3,Div=4;
STACK<int,5> opstk;
STACK<int,6> numstk;
int calc()
{
	opstk.init();
	numstk.init();
	for(int i=1;i<=4;++i)
		numstk.push(num[i]);
	for(int i=1;i<=3;++i)
		opstk.push(op[i]);
	for(int i=1;i<=3;++i)
	{
		int a=numstk.pop(),b=numstk.pop();
		int c=opstk.pop();
		int ans=compute(a,b,c);
		if(ans>0)
		{
			//printf("%d%c%d=%d",a,b,ope[c],ans);
			numstk.push(ans);
		}
		else return 0;
	}
	return numstk.pop();
}

bool formop(int d)
{
	if(d==4)
	{
		if(num[1]==8&&num[2]==6&&num[3]==7&&num[4]==9&&op[1]==3&&op[2]==4&&op[3]==2)
		{
			printf("hello");
		}
		int ans=calc();
		ans=ans;
		return ans==24;
	}
	else
	{
		bool ans=false;
		for(int i=Plus;i<=Div&&!ans;++i)
		{
			op[d]=i;
			if(formop(d+1))ans=true;
		}
		return ans;
	}
}
bool solve()
{
	do
	{
		if(formop(1))
			return true;
	}while(next_permutation(num+1,num+4+1));
	return false;
}
void print()
{
	opstk.init();
	numstk.init();
	for(int i=1;i<=4;++i)
		numstk.push(num[i]);
	for(int i=1;i<=3;++i)
		opstk.push(op[i]);
	for(int i=1;i<=3;++i)
	{
		int a=numstk.pop(),b=numstk.pop();
		int c=opstk.pop();
		int ans=compute(a,b,c);
		if(ans>0)
		{
			printf("%d%c%d=%d\n",a,b,ope[c],ans);
			numstk.push(ans);
		}
	}
}

int main()
{
	freopen("luogu1236#7.in","r",stdin);
	input();
	if(solve())
		print();
	else puts("No answer!");
	return 0;
} 
inline int compute(int x,int y,int op)
{
    if(op==Plus)
    {
        return x+y;
    }
    if(op==Minus)
    {
        return x-y;
    }
    if(op==Mul)
    {
        return x*y;
    }
    if(op==Div)
    {
        return x/y;
    }
}

这种方法表面上看通过改变排列的方式巧妙避开了括号的运算顺序,但是容易找出这种方法不能得到的反例:(a+b)*(c+d)=24。

用搜索改进

解决的办法就是利用深度优先搜索模拟添加括号的顺序,每次枚举当前计算哪个运算符,计算后插入原位置进入下一层,直到只剩一个数与24判断。

需要指出的是,这里仍然要对四个数进行全排列,因为有时候最先运算的两个数可能并不相邻。

代码

当然这样一来就不能用原来的栈实现了,直接使用数组和备份数组模拟计算合并的过程。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
int num[7],a[6],Operator[4];

char ope[5]={'0','+','-','*','/'};
const int Plus=1,Minus=2,Mul=3,Div=4;
bool solved=false;
inline int compute(int x,int y,int op)
{
    if(op==Plus)
    {
        return x+y;
    }
    if(op==Minus)
    {
        return x-y;
    }
    if(op==Mul)
    {
        return x*y;
    }
    if(op==Div)
    {
        return x/y;
    }
}
void print()
{
    solved=true;
    for(int i=1;i<=3;++i)
    {
    	
		if(num[2*i]>num[2*i-1])
		{
			int t=num[2*i];
			num[2*i]=num[2*i-1];
			num[2*i-1]=t;
		}
		
        printf("%d%c%d=%d\n",num[2*i-1],ope[Operator[i]],num[2*i],compute(num[2*i-1],num[2*i],Operator[i]));
    }
    printf("\n");
}
bool operation(int m,int op,int dep)
{
    int n=m+1;
    int ans;
    num[2*dep-1]=a[m];
    num[2*dep]=a[n];
    Operator[dep]=op;
    ans=compute(a[m],a[n],op);
    //if(ans<=0)return false;
    a[m]=abs(ans);
    for(int i=n;i<=4-(dep-1);++i)
    {
        a[i]=a[i+1];
    }
	return true;
}
void search(int k)
{
    if(k==4)
    {
        if(a[1]==24)
        {
            print();
            exit(0);
        }
        else return;
    }
    for(int i=1;i<=4-(k-1)-1;++i)
    {
        for(int o=1;o<=4;++o)
        {
            if(o!=4||(a[i+1]!=0&&a[i]%a[i+1]==0))
            {
            
                int numb[7],ab[7],ob[4];
                memset(numb,0,sizeof(numb));
                memset(ab,0,sizeof(ab));
                memset(ob,0,sizeof(ob));
                memcpy(ab,a,sizeof(a));
                memcpy(numb,num,sizeof(num));
                memcpy(ob,Operator,sizeof(Operator));
                operation(i,o,k);
                //if(operation(i,o,k))
                	search(k+1);
                memcpy(a,ab,sizeof(a));
                memcpy(num,numb,sizeof(num));
                memcpy(Operator,ob,sizeof(Operator));
            }
        }
    }
}
void inPut()
{
    for(int i=1;i<=4;++i)
    {
        scanf("%d",&a[i]);
    }
}
int main()
{
	//freopen("luogu1236.in","r",stdin);
    inPut();
    while(!solved)
    {
    	search(1);
    	if(!solved)
    		next_permutation(a+1,a+1+4);
	}

    if(!solved)
    {
        printf("No answer!\n");
    }
    return 0;
}
0%