题意:有5个数,3种运算符:加、减、乘,用全部5个数,和4个运算符,构成一个表达式,使得值为23,这里没有运算符的优先级,全部是从左往右算。是可以这么理解,题目给的是从左到右依次打了括号。
思路:这里是对表达式的位置进行dfs。可以看到第0位是数字,第1位是运算符,依次则,偶数位是数字,奇数位是运算符。dfs中把当前位置cur分为奇偶分别处理。偶数位置时,则对数字进行枚举,并计算当前表达式的值,注意dfs之后的恢复;奇数位置时,枚举三种运算符。对比可以发现,枚举数字时,由于数字只能出现一次,所以用vis数组标志是否用过;枚举运算符时,由于运算符可多次使用,所以直接枚举即可。另外,初次的dfs调用,由于a[0]前面没有运算符,可默认其为+号,所以初次以dfs(0,'+',0)调用。
还有一种方法是,对每种排列枚举运算符进行运算即可。即调用next_permutation。这里没写。我这里的方法相当于把数字和运算符混在一起进行排列。
解答树的结点个数也可以计算,5!*3^4=9720。测试后发现如果不剪枝,时间有点长。。
注意:开始的思路是在遇到运算符时进行表达式的计算,但这样如果以dfs(0,0,0)初次调用的话,则在a[0]前面也枚举了运算符,是不对的;之后把这一点改了。改成的版本,在1号位没有用,0号和2号位是数字,然后3号位枚举符号,是运算0号和2号的数字的。相当于了后缀表达式形式了。但还是有问题。。。附在最后。
这说明,开始写之前,还是要多想想;否则写出来一个挫的之后,这里发现一问题,那里发现一问题,在它的基础上缝缝补补,就把自己搞得晕头转向了,有时甚至还不如重头重新写,比如这里就是暂时放下了,重新写的,很快就好了。
这里给出一些测试数据:
input:
42 8 2 32 37
10 43 21 46 5
44 2 27 30 29
10 20 20 2 36
28 3 34 42 2
22 6 6 5 37
34 3 31 18 12
25 46 28 13 2
12 4 19 2 50
1 12 2 1 49
48 48 42 2 11
1 2 43 26 33
0 0 0 0 0
output:
Possible
Possible
Impossible
Impossible
Impossible
Impossible
Impossible
Impossible
Impossible
Impossible
Impossible
Impossible
Code:
//AC了
#include<stdio.h>
#include<string.h>
bool dfs(int cur,char c,int cnt);
int a[6];
int vis[6];
//int C[15];
//char F[5];
int main()
{
while(scanf("%d%d%d%d%d",&a[0],&a[1],&a[2],&a[3],&a[4])==5
&&a[0]&&a[1]&&a[2]&&a[3]&&a[4])
{
memset(vis,0,sizeof(vis));
if(dfs(0,'+',0)) printf("Possible\n"); else printf("Impossible\n");
}
return 0;
}
bool dfs(int cur,char c,int cnt)
{
if(cur==9)
{
if(cnt==23)
{
return 1;
}
else return 0;
}
if(cur%2==0)
{//枚举数字
for(int i=0;i<5;++i)
if(!vis[i])
{
vis[i]=1;//选择a[i]
if(c=='+') cnt=cnt+a[i];
else if(c=='-') cnt=cnt-a[i];
else cnt=cnt*a[i];
if(dfs(cur+1,' ',cnt)) return 1;
vis[i]=0;
if(c=='+') cnt=cnt-a[i];
else if(c=='-') cnt=cnt+a[i];
else cnt=cnt/a[i];
}
}
else
{//枚举运算符
if(dfs(cur+1,'+',cnt)) return 1;
if(dfs(cur+1,'-',cnt)) return 1;
if(dfs(cur+1,'*',cnt)) return 1;
}
return 0;
}
AC之后的时间是2.942s,限制是在3s内,这是擦边过啊。于是写了一个剪枝的,时间减少一半多,但还是1.275s,排在1318名。
Code:
//AC了,在上一版本上的剪枝
#include<stdio.h>
#include<string.h>
bool dfs(int cur,char c,int cnt);
int a[6];
int vis[6];
//int C[15];
//char F[5];
int main()
{
while(scanf("%d%d%d%d%d",&a[0],&a[1],&a[2],&a[3],&a[4])==5
&&a[0]&&a[1]&&a[2]&&a[3]&&a[4])
{
memset(vis,0,sizeof(vis));
if(dfs(0,'+',0)) printf("Possible\n"); else printf("Impossible\n");
}
return 0;
}
bool dfs(int cur,char c,int cnt)
{
int temps=0;
for(int i=0;i<5;++i)
if(!vis[i]) temps=temps+a[i];
if(cnt-temps>23) return 0; //剪枝
if(cnt<0 && cnt+temps<23) return 0;
if(cur==9)
{
if(cnt==23)
{
return 1;
}
else return 0;
}
if(cur%2==0)
{//枚举数字
for(int i=0;i<5;++i)
if(!vis[i])
{
vis[i]=1;//选择a[i]
if(c=='+') cnt=cnt+a[i];
else if(c=='-') cnt=cnt-a[i];
else cnt=cnt*a[i];
if(dfs(cur+1,' ',cnt)) return 1;
vis[i]=0;
if(c=='+') cnt=cnt-a[i];
else if(c=='-') cnt=cnt+a[i];
else cnt=cnt/a[i];
}
}
else
{//枚举运算符
if(dfs(cur+1,'+',cnt)) return 1;
if(dfs(cur+1,'-',cnt)) return 1;
if(dfs(cur+1,'*',cnt)) return 1;
}
return 0;
}
最初始的思路,没AC,有问题的Code:
//有问题
//思路是在主函数中枚举第0位的数字。dfs函数中忽略1号位,从2号位开始偶数位枚举数字,
//奇数位枚举运算符。3号位的运算符是计算0号和2号的数字的,相当于了后缀表达式形式。
//但还没有AC,还是有些问题~
#include<stdio.h>
#include<string.h>
bool dfs(int cur,int pre,int cnt);
int a[6];
int vis[6];
int C[15];
char F[5];
int main()
{
while(scanf("%d%d%d%d%d",&a[0],&a[1],&a[2],&a[3],&a[4])==5
&&a[0]&&a[1]&&a[2]&&a[3]&&a[4])
{
//if(dfs(0,0,0)) printf("Possible\n");else printf("Impossible\n");
int flag=0;
for(int i=0;i<4;++i)
{
memset(vis,0,sizeof(vis));
int cnt=a[i];
vis[i]=1;
C[0]=a[i];
if(flag=dfs(1,0,cnt)) break;
}
if(flag) printf("Possible\n"); else printf("Impossible\n");
}
return 0;
}
bool dfs(int cur,int pre,int cnt)
{
if(cur==10)
{
if(cnt==23)
{
for(int i=0;i<4;++i)
printf("%d%c",C[i],F[i+1]);
printf("%d\n",C[4]);
return 1;
}
else return 0;
}
if(cur%2==0)
{//枚举数字
for(int i=0;i<5;++i)
if(!vis[i])
{
vis[i]=1;//选择a[i]
C[cur/2]=a[i];
if(dfs(cur+1,a[i],cnt)) return 1;
vis[i]=0;
}
}
else
{//枚举运算符
if(cur==1) dfs(cur+1,0,cnt);
for(int i=0;i<3;++i)
{
if(i==0)
{ cnt=cnt+pre;
F[cur/2]='+';
if(dfs(cur+1,0,cnt)) return 1; cnt=cnt-pre;}
else if(i==1)
{ cnt=cnt-pre;
F[cur/2]='-';
if(dfs(cur+1,0,cnt)) return 1; cnt=cnt+pre;}
else
{ cnt=cnt*pre;
F[cur/2]='*';
if(dfs(cur+1,0,cnt)) return 1; cnt=cnt/pre;}
}
}
return 0;
}