全自動數字論證機(迫真)
衆所周知,OIer都事HOMO。本小鬼在看到知乎上問題後想起來以前暑假集訓無聊的時候寫的一個數字論證搜索,於是來寫博客
由於python我沒怎麼用過,所以還是用的c++寫的,並且事在晚上訂正完考試寫的,所以稍微有些麻煩。
謝謝茄子!
數據庫生成
大體思路就事生成一個排列,然後通過現成的計算器生成答案並且保存。
生成排列
dfs即可。代碼如下,主要用棧模擬
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<stack>
#include<cstring>
#include<cmath>
#include<windows.h>
#pragma -g -std=c++11
using namespace std;
int t=0;
const int num[7]={0,1,1,4,5,1,4};
char s[10]={0,'+','-','*','/','^','!'};
const int mulj[4][7]={{0,0,0,0,0,0,0},{0,1,1,2,1,1,2},{0,0,0,4,2,0,4},{0,0,0,0,0,0,0}};
const int lenj[7]={0,1,1,2,3,1,2};
string ss;
int maxn=0;
int numb=0;
stack<char>ans;
const int MAX = 30;
const int DONE = 1;
//棧定義
template <class T>
class Stack{
public:
Stack(int MaxStackSize=10);
~Stack() { delete [] stack;}
bool IsEmpty() const {return top==-1;}
bool IsFull() const {return top==MaxTop;}
T Top() const;
Stack<T>& Add(const T& x);
Stack<T>& Del(T& x);
void MakeEmpty(){top=-1;} //清空棧
void print(){
for(int i; i < top + 1; i ++){
cout<<stack[i]<<'\t';
}
cout<<endl;
}
private:
int top;//棧頂
int MaxTop;//最大的棧頂值
T *stack;//堆棧元素數組
};
template<class T>
Stack<T>::Stack(int MaxStackSize){
MaxTop=MaxStackSize-1;
stack=new T[MaxStackSize];
top=-1;
}
template<class T>
Stack<T>& Stack<T>::Add(const T& x){
if(IsFull())
{cout<<"no memory;"<<endl;return *this;}
top=top+1;
stack[top]=x;
return *this;
}
template<class T>
Stack<T>& Stack<T>::Del(T& x){
if(IsEmpty())
{cout<<"no element"<<endl;return *this;}
x=stack[top];
top=top-1;
return *this;
}
template<class T>
T Stack<T>::Top() const{
return stack[top];
}
//判斷一個字符是否爲數字
bool isNum(char c){
if((c > '0'||c == '0')&&(c < '9'||c == '9'))
return true;
else
return false;
}
//刪除字符串中的空格
void deleteBlank(string &s){
string::iterator i = s.begin();
while ((i=find(i, s.end(), ' '))!=s.end())
s.erase(i);
}
//計算器
class Calculator{
public:
Calculator(string s);
~Calculator();
int outPriority(char); //返回棧外優先級
int inPriority(char); //返回棧內優先級
bool judgePri(char, char); //判斷優先級 前一個爲棧外符號,後一個爲棧內符號 若前大於後返回1,否則返回0
int judgePri(char); //判斷運算符 若是'#'返回 -1,若是')'返回 0,否則返回 1
void dealNum(); //處理數據
int calculate(); //計算
void setString(string const s){
this->s = '#' + s + '#';
deleteBlank(this->s); //刪除字符串中的空格
}
private:
Stack<char> *s_sym; //符號棧
Stack<int> *s_num; //數據棧
string s;
};
Calculator::Calculator(string s){
this->s = '#' + s + '#';
deleteBlank(this->s);
s_sym = new Stack<char>(MAX);
s_num = new Stack<int>(MAX);
}
Calculator::~Calculator(){
delete s_sym;
delete s_num;
}
int Calculator::outPriority(char symble){
switch(symble){
case '#':
return 0;
case '(':
return 8;
case '+':
return 2;
case '-':
return 2;
case '*':
return 4;
case '/':
return 4;
case '%':
return 4;
case '^':
return 6;
case ')':
return 1;
default:
throw 1;
}
}
int Calculator::inPriority(char symble){
switch(symble){
case '#':
return 0;
case '(':
return 1;
case '+':
return 3;
case '-':
return 3;
case '*':
return 5;
case '/':
return 5;
case '%':
return 5;
case '^':
return 7;
case ')':
return 8;
default:
throw 1;
}
}
bool Calculator::judgePri(char out, char in){
if(outPriority(out) > inPriority(in))
return true;
else
return false;
}
int Calculator::judgePri(char symble){
if(symble == '#')
return -1;
else if(symble == ')')
return 0;
else
return 1;
}
void Calculator::dealNum(){
//將數據棧中的前兩個彈出進行計算,結果放回數據棧,符號棧彈出頂部元素
char _temp = 0;
int dtemp1 = 0;
int dtemp2 = 0;
s_sym->Del(_temp);
s_num->Del(dtemp1);
s_num->Del(dtemp2);
switch(_temp){
case '+':
dtemp2 += dtemp1;
break;
case '-':
dtemp2 = dtemp2 - dtemp1;
break;
case '*':
dtemp2 = dtemp2 * dtemp1;
break;
case '/':
if(dtemp1 == 0)
throw 0;
else
dtemp2 = dtemp2 / dtemp1;
break;
case '%':
dtemp2 = dtemp2 % dtemp1;
break;
case '^':
dtemp2 = pow(dtemp2,dtemp1);
break;
default:
throw 1;
}
s_num->Add(dtemp2);
}
int Calculator::calculate(){
for(int i = 0; i < s.size(); i ++){ //遍歷字符串
if(isNum(s[i])){
int temp = (int)(s[i]) - 48; //char強制類型轉換爲int ascii 碼數值,減 48 轉換爲對應整數值
int _temp = 0;
if(i > 0 && isNum(s[i - 1])){
s_num->Del(_temp);
temp = _temp * 10 + temp;
}
s_num->Add(temp);
}else{
char temp = s[i];
if(s_sym->IsEmpty()){
s_sym->Add(temp);
}else{
if(judgePri(temp, s_sym->Top())){
s_sym->Add(temp);
}else if(judgePri(temp) == 1){ //棧外優先級小於棧內優先級,且不爲 '#' 和 ')'
while(!judgePri(temp, s_sym->Top())){ //當棧外優先級比棧內優先級低時,執行棧內符號運算
dealNum();
}
s_sym->Add(temp);
}else if (judgePri(temp) == -1){
while(s_sym->Top() != '#'){
dealNum();
}
int result = s_num->Top();
s_sym->MakeEmpty();
s_num->MakeEmpty();
return result;
}
else if(judgePri(temp) == 0){
while(s_sym->Top() != '('){
dealNum();
}
s_sym->Del(temp);
}
}
}
}
}
char nnans[101];
void exc(){
stack<char>st;
while(!ans.empty()){
st.push(ans.top());ans.pop();
}
int rs=-1,sta=1,nnn=0;
ss="";
while(!st.empty()){
char now=st.top();ans.push(now);st.pop();
ss+=now;
}
//Calculator c("");
int po=0;int numc=0;
for(int i=0;i<ss.length();i++){
if('0'<=ss[i]&&ss[i]<='9'&&!('0'<=ss[i+1]&&ss[i+1]<='9')) nnn++;
if(nnn==6) {
po = i;
break;
}
}
int dd=ss.length();
for(int i=po+1;i<dd;i++) ss.pop_back();
for(int i=0;i<ss.length();i++){
if(ss[i]=='(') numc++;
if(ss[i]==')') numc--;
}
while(numc--) ss+=')';
cout<<ss<<endl;
/*c.setString(ss);
printf("%d = ",c.calculate());
nnn=0;
for(int i=0;i<ss.length();i++){
if(nnn==6&&ss[i]!=')') break;
if(ss[i]=='2'&&ss[i+1]=='4'){
printf("4!");i++;nnn++;
}else if(ss[i]=='1'&&ss[i+1]=='2'){
printf("5!");i+=2;nnn++;
}else if(ss[i]=='('&&ss[i+1]==')'){
i++;
}else{
printf("%c",ss[i]);if('1'<=ss[i]&&ss[i]<='9') nnn++;
}
}
putchar(10);*/
}
void out(){
stack<char>st;
while(!ans.empty()){
//printf("%d\n",ans.size());
char sk=ans.top();
st.push(sk);ans.pop();
}
while(!st.empty()){
char now=st.top();ans.push(now);st.pop();
printf("%c",now);
}
putchar(10);
Sleep(500);
}
void dfs(int lev){
if(lev==6){
int tmp=numb;
while(numb--){
ans.push(')');
}
exc();
numb=tmp;
while(tmp--){
ans.pop();
}
return;
}
lev++;
for(int i=1;i<=5;i++){
for(int k=1;k<=lenj[lev];k++) ans.push(mulj[k][lev]+'0');
if(lev!=1) ans.push(')'),numb--;
ans.push(s[i]);
ans.push('('),numb++;
//out();
dfs(lev);
for(int k=1;k<=lenj[lev];k++) ans.pop();
if(lev!=1) ans.pop(),numb++;
ans.pop();
ans.pop();
numb--;
ans.push(char(num[lev]+'0'));
if(lev!=1) ans.push(')'),numb--;
ans.push(s[i]);
ans.push('('),numb++;
//out();
dfs(lev);
if(lev!=1) ans.pop(),numb++;
numb--;
ans.pop();
ans.pop();
ans.pop();
for(int k=1;k<=lenj[lev];k++) ans.push(mulj[k][lev]+'0');
ans.push(s[i]);
ans.push('('),numb++;
//out();
dfs(lev);
for(int k=1;k<=lenj[lev];k++) ans.pop();
ans.pop();
numb--;
ans.pop();
ans.push(num[lev]+'0');
ans.push(s[i]);
ans.push('('),numb++;
//out();
dfs(lev);
ans.pop();
ans.pop();
ans.pop();
numb--;
}
return;
}
int main(){
freopen("1.txt","w",stdout);
dfs(0);
return 0;
}
計算答案並輸出數據庫
去重是一個很重要的問題。用map會RE,我只開了個2e7的桶來簡單去重。並且注意到重複一般都聚集在一起(搜索的時候一些相近的生成方案),記錄當前答案之前的幾個答案即可。
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include<map>
using namespace std;
const char Tab=0x9;
const int DIGIT=1;
const int MAXLEN=16384;
char s[MAXLEN],*endss;
int pcs=15;
double fun(double x,char op[],int *iop) {
while (op[*iop-1]<32) //本行使得函數嵌套調用時不必加括號,如 arc sin(sin(1.234)) 只需鍵入arc sin sin 1.234<Enter>
switch (op[*iop-1]) {
case 7: x=sin(x); (*iop)--;break;
case 8: x=cos(x); (*iop)--;break;
case 9: x=tan(x); (*iop)--;break;
case 10: x=sqrt(x); (*iop)--;break;
case 11: x=asin(x); (*iop)--;break;
case 12: x=acos(x); (*iop)--;break;
case 13: x=atan(x); (*iop)--;break;
case 14: x=log10(x);(*iop)--;break;
case 15: x=log(x); (*iop)--;break;
case 16: x=exp(x); (*iop)--;break;
}
return x;
}
double calc(char *expr,char **addr) {
static int deep; //遞歸深度
static char *fname[]={ "sin","cos","tan","sqrt","arcsin","arccos","arctan","lg","ln","exp",NULL};
double ST[10]={0.0}; //數字棧
char op[10]={'+'}; //運算符棧
char c,*rexp,*pp,*pf;
int ist=1,iop=1,last,i;
if (!deep) {
pp=pf=expr;
do {
c = *pp++;
if (c!=' '&& c!=Tab)
*pf++ = c;
} while (c!='\0');
}
pp=expr;
if ((c=*pp)=='-'||c=='+') {
op[0] = c;
pp++;
}
last = !DIGIT;
while ((c=*pp)!='\0') {
if (c=='(') {//左圓括弧
deep++;
ST[ist++]=calc(++pp,addr);
deep--;
ST[ist-1]=fun(ST[ist-1],op,&iop);
pp = *addr;
last = DIGIT;
if (*pp == '('||isalpha(*pp) && strnicmp(pp,"Pi",2)) {//目的是:當右圓括弧的右惻爲左圓括弧或函數名字時,默認其爲乘法
op[iop++]='*';
last = !DIGIT;
c = op[--iop];
goto operate ;
}
}
else if (c==')') {//右圓括弧
pp++;
break;
} else if (isalpha(c)) {
if (!strnicmp(pp,"Pi",2)) {
if (last==DIGIT) {
return -1;
}
ST[ist++]=3.14159265358979323846264338328;
ST[ist-1]=fun(ST[ist-1],op,&iop);
pp += 2;
last = DIGIT;
if (!strnicmp(pp,"Pi",2)) {
return -1;
}
if (*pp=='(') {
return -1;
}
} else {
for (i=0; (pf=fname[i])!=NULL; i++)
if (!strnicmp(pp,pf,strlen(pf))) break;
if (pf!=NULL) {
op[iop++] = 07+i;
pp += strlen(pf);
} else {
return -1;
}
}
} else if (c=='+'||c=='-'||c=='*'||c=='/'||c=='^') {
char cc;
if (last != DIGIT) {
return -1;
}
pp++;
if (c=='+'||c=='-') {
do {
cc = op[--iop];
--ist;
switch (cc) {
case '+': ST[ist-1] += ST[ist];break;
case '-': ST[ist-1] -= ST[ist];break;
case '*': ST[ist-1] *= ST[ist];break;
case '/': ST[ist-1] /= ST[ist];break;
case '^': ST[ist-1] = pow(ST[ist-1],ST[ist]);break;
}
} while (iop);
op[iop++] = c;
} else if (c=='*'||c=='/') {
operate: cc = op[iop-1];
if (cc=='+'||cc=='-') {
op[iop++] = c;
} else {
--ist;
op[iop-1] = c;
switch (cc) {
case '*': ST[ist-1] *= ST[ist];break;
case '/': ST[ist-1] /= ST[ist];break;
case '^': ST[ist-1] = pow(ST[ist-1],ST[ist]);break;
}
}
} else {
cc = op[iop-1];
if (cc=='^') {
return -1;
}
op[iop++] = c;
}
last = !DIGIT;
} else {
if (last == DIGIT) {
return -1;
}
ST[ist++]=strtod(pp,&rexp);
ST[ist-1]=fun(ST[ist-1],op,&iop);
if (pp == rexp) {
return -1;
}
pp = rexp;
last = DIGIT;
if (*pp == '('||isalpha(*pp)) {
op[iop++]='*';
last = !DIGIT;
c = op[--iop];
goto operate ;
}
}
}
*addr=pp;
if (iop>=ist) {
exit(0);
}
while (iop) {
--ist;
switch (op[--iop]) {
case '+': ST[ist-1] += ST[ist];break;
case '-': ST[ist-1] -= ST[ist];break;
case '*': ST[ist-1] *= ST[ist];break;
case '/': ST[ist-1] /= ST[ist];break;
case '^': ST[ist-1] = pow(ST[ist-1],ST[ist]);break;
}
}
return ST[0];
}
int mp[20000000];
long long pr1=-114514,pr2=-1919810;
int main() {
freopen("1.txt","r",stdin);
freopen("senpai_database.txt","w",stdout);
while (1) {
gets(s);
double resu=calc(s,&endss);if(resu==-1||resu<0||(resu-floor(resu)>0)||(resu<=2e7&&(mp[int(resu)]))){
continue;
}else if(((long long)resu==pr1)||((long long)resu==pr2)){
pr2=pr1,pr1=(long long)(resu);continue;
}
if(resu<=2e7) mp[(int)resu]=1;
printf("%lld = ",(long long)resu);
long long nnn=0,cntt=0;
int nn=strlen(s);
for(long long i=0;i<nn;i++){
if(nnn==6&&s[i]!=')') break;
if(s[i]=='2'&&s[i+1]=='4'){
printf("4!");i++;nnn++;
}else if(s[i]=='1'&&s[i+1]=='2'){
printf("5!");i+=2;nnn++;
}else{
printf("%c",s[i]);if('1'<=s[i]&&s[i]<='9') nnn++;
}
if(s[i]=='(') cntt++;
if(s[i]==')') cntt--;
}
while(cntt--){
printf(")");
}
putchar(10);
pr2=pr1,pr1=(long long)(resu);
}
return 0;
}
查詢數據庫
如果能排序二分的話能省不少時間,但是數據量太大了,我就直接FOR過去了。實測字母少的時候很快。
/* 棧實現計算器,主要思路就是設置一個符號棧和一個數字棧,在字符串首尾各加一個'#',然後掃描字符串,
* 如果是數字進數字棧,如果是運算符號先判斷符號優先級,若棧外符號優先級大於棧內符號優先級則進棧,
* 小於棧內優先級則符號棧出棧一位,數字棧出棧兩位進行計算,結果重新存進數字棧,直到棧外優先級大於棧內,
*/
#include <iostream>
#include <string>
#include <algorithm>
#include <cmath>
#include<cstring>
using namespace std;
const long long MAX = 30;
const long long DONE = 1;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
//棧定義
template <class T>
class Stack{
public:
Stack(long long MaxStackSize=10);
~Stack() { delete [] stack;}
bool IsEmpty() const {return top==-1;}
bool IsFull() const {return top==MaxTop;}
T Top() const;
Stack<T>& Add(const T& x);
Stack<T>& Del(T& x);
void MakeEmpty(){top=-1;} //清空棧
void print(){
for(long long i; i < top + 1; i ++){
cout<<stack[i]<<'\t';
}
cout<<endl;
}
private:
long long top;//棧頂
long long MaxTop;//最大的棧頂值
T *stack;//堆棧元素數組
};
template<class T>
Stack<T>::Stack(long long MaxStackSize){
MaxTop=MaxStackSize-1;
stack=new T[MaxStackSize];
top=-1;
}
template<class T>
Stack<T>& Stack<T>::Add(const T& x){
if(IsFull())
{cout<<"no memory;"<<endl;return *this;}
top=top+1;
stack[top]=x;
return *this;
}
template<class T>
Stack<T>& Stack<T>::Del(T& x){
if(IsEmpty())
{cout<<"no element"<<endl;return *this;}
x=stack[top];
top=top-1;
return *this;
}
template<class T>
T Stack<T>::Top() const{
return stack[top];
}
//判斷一個字符是否爲數字
bool isNum(char c){
if((c > '0'||c == '0')&&(c < '9'||c == '9'))
return true;
else
return false;
}
//刪除字符串中的空格
void deleteBlank(string &s){
string::iterator i = s.begin();
while ((i=find(i, s.end(), ' '))!=s.end())
s.erase(i);
}
//計算器
class Calculator{
public:
Calculator(string s);
~Calculator();
long long outPriority(char); //返回棧外優先級
long long inPriority(char); //返回棧內優先級
bool judgePri(char, char); //判斷優先級 前一個爲棧外符號,後一個爲棧內符號 若前大於後返回1,否則返回0
long long judgePri(char); //判斷運算符 若是'#'返回 -1,若是')'返回 0,否則返回 1
void dealNum(); //處理數據
long long calculate(); //計算
void setString(string const s){
this->s = '#' + s + '#';
deleteBlank(this->s); //刪除字符串中的空格
}
private:
Stack<char> *s_sym; //符號棧
Stack<long long> *s_num; //數據棧
string s;
};
Calculator::Calculator(string s){
this->s = '#' + s + '#';
deleteBlank(this->s);
s_sym = new Stack<char>(MAX);
s_num = new Stack<long long>(MAX);
}
Calculator::~Calculator(){
delete s_sym;
delete s_num;
}
long long Calculator::outPriority(char symble){
switch(symble){
case '#':
return 0;
case '(':
return 8;
case '+':
return 2;
case '-':
return 2;
case '*':
return 4;
case '/':
return 4;
case '%':
return 4;
case '^':
return 6;
case ')':
return 1;
default:
throw 1;
}
}
long long Calculator::inPriority(char symble){
switch(symble){
case '#':
return 0;
case '(':
return 1;
case '+':
return 3;
case '-':
return 3;
case '*':
return 5;
case '/':
return 5;
case '%':
return 5;
case '^':
return 7;
case ')':
return 8;
default:
throw 1;
}
}
bool Calculator::judgePri(char out, char in){
if(outPriority(out) > inPriority(in))
return true;
else
return false;
}
long long Calculator::judgePri(char symble){
if(symble == '#')
return -1;
else if(symble == ')')
return 0;
else
return 1;
}
void Calculator::dealNum(){
//將數據棧中的前兩個彈出進行計算,結果放回數據棧,符號棧彈出頂部元素
char _temp = 0;
long long dtemp1 = 0;
long long dtemp2 = 0;
s_sym->Del(_temp);
s_num->Del(dtemp1);
s_num->Del(dtemp2);
switch(_temp){
case '+':
dtemp2 += dtemp1;
break;
case '-':
dtemp2 = dtemp2 - dtemp1;
break;
case '*':
dtemp2 = dtemp2 * dtemp1;
break;
case '/':
if(dtemp1 == 0)
throw 0;
else
dtemp2 = dtemp2 / dtemp1;
break;
case '%':
dtemp2 = dtemp2 % dtemp1;
break;
case '^':
dtemp2 = pow(dtemp2,dtemp1);
break;
default:
throw 1;
}
s_num->Add(dtemp2);
}
long long Calculator::calculate(){
for(long long i = 0; i < s.size(); i ++){ //遍歷字符串
if(isNum(s[i])){
long long temp = (long long)(s[i]) - 48; //char強制類型轉換爲long long ascii 碼數值,減 48 轉換爲對應整數值
long long _temp = 0;
if(i > 0 && isNum(s[i - 1])){
s_num->Del(_temp);
temp = _temp * 10 + temp;
}
s_num->Add(temp);
}else{
char temp = s[i];
if(s_sym->IsEmpty()){
s_sym->Add(temp);
}else{
if(judgePri(temp, s_sym->Top())){
s_sym->Add(temp);
}else if(judgePri(temp) == 1){ //棧外優先級小於棧內優先級,且不爲 '#' 和 ')'
while(!judgePri(temp, s_sym->Top())){ //當棧外優先級比棧內優先級低時,執行棧內符號運算
dealNum();
}
s_sym->Add(temp);
}else if (judgePri(temp) == -1){
while(s_sym->Top() != '#'){
dealNum();
}
long long result = s_num->Top();
s_sym->MakeEmpty();
s_num->MakeEmpty();
return result;
}
else if(judgePri(temp) == 0){
while(s_sym->Top() != '('){
dealNum();
}
s_sym->Del(temp);
}
}
}
}
}
int tt=0;
char t[1001];
int main() {
//freopen("1.txt","r",stdin);
scanf("%s",t);
freopen("senpai_database.txt","r",stdin);
//freopen("out.txt","w",stdout);
char s[1001];
int li=strlen(t);
int key=0;
for(int i=0;i<li;i++){
printf("%c",t[i]);
}
printf(" = ");
for(int i=0;i<li;i++){
int p=t[i]-'a'+1>0?t[i]-'a'+1:t[i]-'0';
if(li!=1) printf("%d",p);
if(i<li-1) printf("+");
key+=p;
}
if(li!=1) printf(" = ");//return 0;
while(gets(s)){
int res=0,pos=0,ll=strlen(s);
while(isdigit(s[pos])&&pos<ll){
res=(res<<1)+(res<<3)+s[pos++]-'0';
}
if(s[0]==0) return 0;
if(key==res){
for(int i=0;i<ll;i++){
printf("%c",s[i]);
}
return 0;
}
s[0]=0;
}
return 0;
}
用法:直接輸入字母,如下:
如果想要自己嘗試請依次編譯運行上面三個,不過耗時會有點久,還佔內存(悲)