码迷,mamicode.com
首页 > 其他好文 > 详细

UVa 10344 算23点

时间:2015-03-03 22:13:21      阅读:271      评论:0      收藏:0      [点我收藏+]

标签:uva   暴力求解   回溯   递归   剪枝   

题意:有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;
}




UVa 10344 算23点

标签:uva   暴力求解   回溯   递归   剪枝   

原文地址:http://blog.csdn.net/buxizhizhou530/article/details/44042941

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!