小兔生仔和汽水换瓶的两个算法

一、小兔生仔

本来我想找到小兔生仔的常数算法,因为我认为只要不断展开递归式就能得到常数算法。

但是自己分析基础太差,不得要领,但是中途冒出了一个算法,可以得到线性算法。

小兔子生仔看上去就是个递归的复杂结构,但是其实小兔子只有13种状态(12个半年),因为它的一生很短暂。

0.0 小兔子

0.5 小兔子

1.0 大兔子

1.5 大兔子 生兔子

2.0 大兔子 生兔子 (2.0生有2个兔子)

2.5 大兔子 生兔子

3.0 大兔子 生兔子

3.5 大兔子 生兔子

4.0 大兔子 生兔子

4.5 大兔子 生兔子

5.0 大兔子 生兔子

5.5 大兔子

6.0 大兔子 (>=6.0兔子死亡)

这十三个状态就是一个先进先出的队列,随着年龄的增长,状态只有向后迁移。

而新加入的状态(新生兔子),就是8个繁殖期状态的总数。

而最后的状态,就是死亡的状态。

我们要知道总共有多少个兔子还活着,只需要统计除了最后一格的总数。而不管n等于多少,只需要更新2*n次状态,便可以。

代码:

//用循环状态数组解决小兔生仔问题
  //0,1,2,3,4,5,6,7,8,9,10,11,12
  static int 小兔生仔( uint n )
  {
      //初始化
      uint[] states = new uint[13];
      //状态循环
      int start = 0;//0 ~ 11
      states[start] = 1; //初始的小兔子是0岁
      //n次长大后
      for ( int i = 0; i < n; i++ )
      {
          //增加年龄
          AddAge(ref start);

          //更新状态
          states[start] = states[getIndex( start, 10 )] + states[getIndex( start, 3 )] + states[getIndex( start, 4 )] + states[getIndex( start, 5 )]
               + states[getIndex( start, 6 )] + states[getIndex( start, 7 )] + states[getIndex( start, 8 )] + states[getIndex( start, 9 )];
          
          //调试
          Console.Write("小兔生仔第" + ((float)(i+1) / 2).ToString("{0.0}") + "年:");
          for ( int j = 0; j < 13; j++ )
          {
              Console.Write(states[getIndex(start, j)] + ", ");
          }
          Console.WriteLine();
      }
      //统计结果
      uint count = 0;
      for ( int i = 0; i < 12; i++ )
      {
          count += states[getIndex( start, i )];
      }
      return (int)count;
  }

  static void AddAge( ref int index )
  {
      index--;
      if ( index < 0 )
          index += 13;
  }

  static int getIndex( int start, int index )
  {
      return (start + index)%13;
  }

二、汽水换瓶

汽水换瓶也是个递归的结构,但是通过简单的代数变换,可以得到一个非递归的公式。

3空瓶 = 1水 +1空瓶 --》2空瓶 = 1水

问题是这种置换有个前提条件,那就是置换的时候需要保证先有3个空瓶以上,否则前提条件不满足,后面的公式也就没有意义。

如何保证至少有三个空瓶呢?可以先拿3个空瓶出来,放在一边,997/2.....1 + 3=4 剩余4个,4个根据正规公式很容易就能算出最后剩下两个空瓶。

其实可以再进一步,只需要拿一个空瓶出来就可以了,因为转换之前至少已经有2个空瓶了,加上你留出来的那一个,就更好三个。

所以: 999/2=499....1 + 1 = 余2.

喝的汽水:499+1000=1499.

这个算法的好处是你不需要担心余数加起来又要算一次。

代码:

static Tuple<int,int>  汽水换瓶( int n,int p,int k )
{
Tuple<int, int> t = 空瓶换汽水( n, p,k );
Tuple<int, int> result = new Tuple<int, int>( t.Item1 + n, t.Item2 );
return result;
}

//n = 空瓶数
//p个空瓶换 k 瓶汽水
private static Tuple<int,int> 空瓶换汽水( int n, int p, int k )
{
int 汽水 = ( n - k ) / ( p - k );
int 余瓶 = k + (n-k) % ( p - k );
return new Tuple<int,int>(汽水, 余瓶);
}
原文地址:https://www.cnblogs.com/Nobel/p/2184270.html