简述
写这个是因为最初的时候想模仿着写个1145141919论证器,但在放一堆括号优先级的时候写崩了,想不到好思路。然后去洛谷上刷了算24点(绿题)想找找思路(因为24点也有个插入括号优先级的操作),没想到题解竟然全是括号优先级全排列…真就直接硬打表呗。然后我的1145141919论证器就暂时退化成了24点计算器(笑)。
200行左右,可做C语言大作业(笑)
思路分析
24点游戏大概就是给你四个数字,然后让你通过自定义加减乘除顺序和运算顺序来凑到24。
我的方法总体是全排列(数字)套全排列(运算符)套全排列(括号优先级)
先对四个数字进行dfs全排列,然后对于每一个排列,再dfs插入运算符(+,-,*,/),然后再将插完运算符的表达式全排列,按照不同的优先级插入括号(见代码),然后计算出结果存起来即可。
计算的方法就是把构造好的表达式直接套进个栈模拟计算机处理算数括号表达式(OVS&OPS),这个是栈的一个经典应用案例。
代码
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
long double OVS[100],tns=24.0;
char OPS[100];
string st[15],str[15];
string s,s1;
int p,i,j,t,tmp;
int vis[10]={0},u[10]={0};
vector<string> words;
struct pari{
string s;
long double x;
};
vector<pari> ans; //存答案
struct Node{
char ch;
int p;
};
Node a[15]; //存运算符及其在s中的位置
int cmp(pari a,pari b){
return a.x<b.x;
}
void push(char ch) { //运算符入栈运算
p++;
OPS[p]=ch;
}
void pop() { //栈顶运算符出栈,且将运算好的结果重新入OVS
p--;
switch (OPS[p + 1]) {
case '+':OVS[p]+=OVS[p+1]; break;
case '-':OVS[p]-=OVS[p+1]; break;
case '*':OVS[p]*=OVS[p+1]; break;
case '/':OVS[p]/=OVS[p+1]; break;
}
}
bool ok() { //判断运算符优先级
if ((s[i]=='+' || s[i]=='-') && (OPS[p]!='(')) return 1; //括号优先级更高运算
if ((s[i]=='*' || s[i]=='/') && (OPS[p]=='*' || OPS[p]=='/')) return 1; //乘除同级运算
return 0;
}
int trans(int x, int y) { //将字符串转换为操作数
int tot=0, m;
for (int i=x; i<=y; i++) {
m=s[i]-'0';
tot+=m*pow(10,y-i);
}
return tot;
}
void calculate(){
/*栈处理括号表达式计算*/
s='('+s+')';
i=0; p=0;
while (i<s.length()) {
while (s[i]=='(') { //左括号处理
push(s[i]);
i++;
}
j=i;
while (s[i]<='9' && s[i]>='0') i++;
tmp=trans(j,i-1);
OVS[p] = tmp; //取操作数入栈
do {
if (s[i]==')') { //右括号处理
while (OPS[p]!='(') pop();
p--;
OVS[p]=OVS[p+1];
}
else {
while (ok()) pop(); //根据优先级左入栈/出栈运算处理
push(s[i]);
}
i++;
} while (i<s.length() && s[i-1]==')');
}
pari u;
u.s=s; u.x=OVS[0];
ans.pb(u);
}
void makeit(int l){
/*枚举各种括号优先级下的情况*/
s='('+words[0]+words[1]+words[2]+')'+words[3]+words[4]+words[5]+words[6]; //(a?b)?c?d
calculate();
s='('+words[0]+words[1]+words[2]+words[3]+words[4]+')'+words[5]+words[6]; //(a?b?c)?d
calculate();
s=words[0]+words[1]+'('+words[2]+words[3]+words[4]+')'+words[5]+words[6]; //a?(b?c)?d
calculate();
s=words[0]+words[1]+'('+words[2]+words[3]+words[4]+words[5]+words[6]+')'; //a?(b?c?d)
calculate();
s=words[0]+words[1]+words[2]+words[3]+'('+words[4]+words[5]+words[6]+')'; //a?b?(c?d)
calculate();
s="(("+words[0]+words[1]+words[2]+')'+words[3]+words[4]+')'+words[5]+words[6]; //((a?b)?c)?d
calculate();
s='('+words[0]+words[1]+'('+words[2]+words[3]+words[4]+')'+')'+words[5]+words[6]; //(a?(b?c))?d
calculate();
s=words[0]+words[1]+'('+'('+words[2]+words[3]+words[4]+')'+words[5]+words[6]+')'; //a?((b?c)?d)
calculate();
s=words[0]+words[1]+'('+words[2]+words[3]+'('+words[4]+words[5]+words[6]+')'+')'; //a?(b?(c?d))
calculate();
s='('+words[0]+words[1]+words[2]+')'+words[3]+'('+words[4]+words[5]+words[6]+')'; //(a?b)?(c?d)
calculate();
}
void dfs(int k,int t,int l){ //枚举运算符
if (k>3 && t<=l) return;
if (t>l){
/*将插入完符号后的表达式合成字符串s*/
s.clear();
for (int i=1; i<=3; i++)
s+=st[i-1]+a[i].ch;
s+=st[3];
s1=s;
/*将字符串中的数字和字符分离并且存入words中*/
words.clear();
int tt=0,start=0,kk=0;
while (start<s.length()){
if (s[start]>'9' || s[start]<'0'){
words.pb(s.substr(kk,start-kk));
words.pb(s.substr(start,1));
kk=start+1;
}
start++;
}
words.pb(s.substr(kk,s.length()-kk));
calculate(); //a?b?c?d
makeit(l);
return;
}
else{
for (int i=1; i<=4; i++){
if (i==1 && t<=l && !vis[k]){ //+
vis[k]=1;
a[t].ch='+'; a[t].p=k;
dfs(k+1,t+1,l); vis[k]=0;
}
if (i==2 && t<=l && !vis[k]){ //*
a[t].ch='*'; a[t].p=k; vis[k]=1;
dfs(k+1,t+1,l); vis[k]=0;
}
if (i==3 && t<=l && !vis[k]){ //-
a[t].ch='-'; a[t].p=k; vis[k]=1;
dfs(k+1,t+1,l); vis[k]=0;
}
if (i==4 && t<=l && !vis[k]){ ///
a[t].ch='/'; a[t].p=k; vis[k]=1;
dfs(k+1,t+1,l); vis[k]=0;
}
}
}
}
void dfs1(int k){
if (k>3){
dfs(0,1,3); //深搜枚举运算符
return;
}
else{
for (int i=0; i<4; i++){
if (!u[i]){
st[k]=str[i]; u[i]=1; dfs1(k+1); u[i]=0;
}
}
}
}
int main(){
cin>>t;
while (t--){
ans.clear();
for (int i=0; i<4; i++)
cin>>str[i];
dfs1(0); //全排列四个数字
/*二分查找找出任意解*/
sort(ans.begin(),ans.end(),cmp);
int l=0,r=ans.size(),okk=0;
while (l+1<r){
int mid=(l+r)/2;
if (trunc(ans[mid].x*100)==2400){
cout<<ans[mid].s.substr(1,ans[mid].s.length()-2)<<"=24"<<endl; //输出答案
okk=1;
break;
}
if (ans[mid].x>24)
r=mid;
else
l=mid;
}
if (!okk)
cout<<"No answer!"<<endl;
}
return 0;
}