翻硬币问题的多种求解

来源:互联网 发布:post js加密 编辑:程序博客网 时间:2024/06/03 11:00

[问题描述]

         

一个关于翻硬币的问题,
一摞硬币共有m枚,每一枚都是正面朝上。取下最上面的一枚硬币,将它翻面后放回原处。然后取下最上面的2枚硬币,将他们一起翻面后再放回原处。再取3枚,取4枚……直至m枚。然后再从这摞硬币最上面的一枚开始,重复刚才的做法。这样一直做下去,直到这摞硬币中的每一枚又都是正面朝上为止。例如,m为1时,翻两次即可。m为2时,翻3次即可;m为3时,翻9次即可;m为4时,翻11次即可;m为5时,翻24次即可;…;m为30时,翻899次即可;…
输 入:
  仅有的一个数字是这摞硬币的枚数m,0<m<1000。
  输 出:
  为了使这摞硬币中的每一枚又都是正面朝上所必需翻的次数。
  某单元输入:
      30
  某单元格等于:
      899
 
下面是我自己的两种解法
[解法一]
思路:每一轮的翻转后,能得到每一枚硬币当前的位置的位置,如果硬币重新回到原先的位置(基数个硬币时)或者相反的位置(偶数个硬币时),就的到了求解。
        //查找每轮循环后的位置
        int GetTurnAfterPos(int n, int turn, int m)
        {
               int TurnNum ;//每轮翻转次数
               int TurnAfterPos; //每轮循环后的位置
              TurnNum = turn - n + 1;
              if(TurnNum % 2 == 0) //为偶数次循环
                     TurnAfterPos = n + TurnNum / 2;
              else                //为奇数次循环
                     TurnAfterPos = (TurnNum + 1) / 2;
              return TurnAfterPos;
        }
        //每一轮翻转之后的位置:
        int solve(int turn, int * CurrentPos, int m)
        {
             if(turn == 0)
                    return 0;
             for(int i = 0; i < turn; i ++)
             {
                    int RetNum = GetTurnAfterPos(i+1, turn, m);
                    CurrentPos[RetNum-1] = CurrentPos[m+i];
             }
             for(i = 0; i < m; i ++)
            {
                   CurrentPos[i+m] = CurrentPos[i];
             }
             return 0;
       }
       int main()
       {
              int m = 30;//硬币总数 
              int * CurrentPos = new int[m*2]; 
              int n = 0;//翻转次数
              bool flag = false;
              do
             {  
                   n++;
                   for(int i = 0; i < m*2; i++)
                           CurrentPos[i] = i % m + 1;
                  int x = n / m;
                  int y = n % m;
                  for(i = 0; i < x; i++)
                          solve(m, CurrentPos, m);
  
                  solve(y, CurrentPos, m);
                  for(i = 0; i < m; i ++)
                  {
                           printf("%d ",CurrentPos[i]); 
                  }
                  //solve(5, CurrentPos, m);
                  printf("/n");
                  for(i = 0; i < m; i++)
                 {
                           if(CurrentPos[i] != i+1)
                           {
                                 break;
                           }  
                           if(i == m - 1 && n > 1)
                                 flag = true;
                }  
          }
          while(!flag);
               if(m % 2 == 0)
                      printf("翻转的总次数是:    %d/n", n - 1); 
              else
                     printf("翻转的总次数是:    %d/n", n); 
              delete []CurrentPos;
              return 0;
     }
[解法二]
思路:不管硬币的次序,只是记住每次的翻转和交换,硬币的初始状态都是TRUE,每个COIN经过多伦的翻转后都变回TRUE,那么就求出了解,这是最简单的思路。
     void solve(int m)
    {
          bool * CurrentSurface = new bool[m];
          for(int i = 0; i < m; i++)
          CurrentSurface[i] = true;
         int turnTimes = 0;//翻转次数
         bool bSuccess = false;
         do
         {
              for(int i = 0; i < m; i++)
              {
                     for(int j = 0; j < (i+1) / 2; j ++)
                    {
                             //turnCoin(j, i - j + 1);
                             bool temp = CurrentSurface[j];
                             CurrentSurface[j] = !CurrentSurface[i - j];
                             CurrentSurface[i - j] = !temp;
                    }
                    if((i+1) % 2 == 1)
                             CurrentSurface[i/2] = !CurrentSurface[i/2];  
                   turnTimes=turnTimes+1;
                   bSuccess = true;
                   for(int n = 0; n < m; n ++)
                   {
                        if (CurrentSurface[n] == false)
                        {
                             bSuccess = false;
                             break;
                        }
                   }
                   if(bSuccess)
                        break;
              }
         }while(!bSuccess);
         printf("翻转的总次数是:    %d/n", turnTimes);
         delete [] CurrentSurface;
}
 
int main()
{
     int CoinCount = 1;
     while(CoinCount > 0)
     {
          printf("请输入硬币的总数:");
          scanf("%d",&CoinCount);
          solve(CoinCount);
     }      
      return 0;
}
 
网上还有几种解法,也同时列了出来:
[解法三]
这个是最难理解的一个算法,因为没有注释,呵呵。
 
int solve(int  m); 
int main() 
{  
     int  m;  
     do  {  
              scanf("%d",  &m);  
              if(m  >  0  &&  m  <  1000)  
              printf("%d/n/n",  solve(m));  
     }  while(m  >  0  &&  m  <  1000); 

int  solve(int  m) 
{  
     int  I,  
     t, 
     d, 
     s = -  1; //翻转的次数 
    int  flag;
    //如果只有一枚硬币,翻两次就能达到目标
    if(m == 1) 
         s = 2; 
    else
   { 
         d = 2  *  m  +  1;    //确定硬币是经过偶数次翻转还是奇数次翻转
         t = 2;        //表示一个COIN必须翻转偶数次,才能从正面继续翻回到正面。 
         I = 1;        //翻转的轮数,每轮为从1翻转到m
         flag = 0;    //退出循环标志,翻转完成标志
  
         printf("d  =  %d:/n",  d);  
         do { 
              printf("/tt(%d)  =  %d,  /t  s:  %3d,  %3d/n",  I,  t,  I  *  m,  I  *  m  -  1); 
              if(t == 1)            //
              {  
                       s = I * m; 
                       flag = 1; 
              } 
              else if(t == 2 * m)      { 
                      s = I * m - 1; 
                      flag = 1; 
             } 
             else                      
             {
                     t = (t  *  2)  %  d; 
             }
             I = I + 1; 
       }  while(!flag); 
   } 
   printf("s  =  %d,  I  =  %d,  t  =  %d,  d  =  %d/n",  s,  I,  t,  d); 
   return  s; 
}
 
[解法四]
 思路: 设 题设的从1到n的翻转为一轮番转,位置   pos,   turnNum   为位置pos所需要的翻转次数 ,假设有5个硬币,我们按其位置分别编号   1,2,3,4,5 经过一轮翻转后,变成了5,3,1,2,4;可以看到原来1位置的硬币现在到了3。这一点说明一轮翻转过后,pos的排列是有规律的,另外,任意位置上的元素的翻转次数为turnNum   =     num+1-pos;   
由以上分析得到了,任意位置pos,经过一轮番转后的位置变为了new_pos   =   num/2   +   1   +   turnNum/2   -   turnNum%2*turnNum   所以,可以由上式计算出new_pos。那么,如果new_pos也为1的话,则所有的硬币都回到了原来的位置,且都朝正面。   
  #include   "stdio.h"  
  int   solve(int   m)  
  {  
          int   I   =   0;  
          int   pos,turnNum,temp,s;  
          turnNum   =   0;  
          if(m   ==   1)   s   =   2;  
          else  
          {  
                  pos   =   1;  
                  I   =   0;  
                  do  
                  {  
                          I++;  
                          temp   =   m+1-pos;  
                          turnNum   +=   temp;  
                          pos   =   m/2   +   1   +   pos/2   -   temp%2*(pos/2)*2;  
                  }while(pos!=1);  
                  s   =   I*m   -   turnNum%2;         //the   sum   of   times  
          }  
          return   s;  
  }  
   
  main()  
  {  
          int   m;  
          do  
          {       scanf("%d",&m);  
                  printf("the   sum   is   :   %d/n",solve(m));  
          }while(m   !=   -1);  
  }   
  
[解法五]
      思路: 直接模拟
      #include<iostream>  
      #include<stdlib.h>  
      #define   swap(a,b){int   t=!a;   a=!b;   b   =   t;}   
      int     n;  
      int   flag[1000];  
       
       
      bool   isright()  
      {            
                for(int   i=0;   i<n;   i++)    
                        if(!flag[i])  
                            return   false;  
                return   true;              
      }    
     
      int   turncoin()  
      {          
              int   total   =   1;  
                       
              while(1)  
              {  
                    int   k   =   total%n;  
                    int   i,j;  
                    for(   i=0,j=k;   i<=j;   i++,   j--)  
                          swap(flag[i],flag[j]);  
                    total++;                  
                    if(isright())  
                          return   total;        
              }  
              return   total;  
      }  
       
      void   init()  
      {          
     
                  for(int   i=0;   i<n;   i++)    
                        flag[i]   =   1;    
                flag[0]   =   0;  
     
      }  
      int   main()  
      {  
               
    while(std::cin >> n && n>0)  
              {          
                    init();    
     std::cout << turncoin() << std::endl;              
              }  
              return   0;  
                             
      }