一个翻牌算法

本周四同事分享了一个思维训练的PPT,里面有一个关于翻牌的题目,题目大致是:拿出从A到10的10张扑克牌,背面朝上摞在一起。首先把最上面的一张挪到下面,掀开新出现的一张牌是A,取出,再挪一张牌到下面,翻一张是2,依次类推,可以有顺序地翻出A到10的牌来。请问这10张牌最初是怎么排列的?看完这个题目,我当时说可以用一个算法实现。

第二天6点多醒来就一直在想这个问题,开始的时候想用递归实现,最后发现有点复杂,自己实现不了,然后想用数组实现,想法大致是这样的,先将这N个数存到数组中,然后将第一张插到最后面,第二张为A,以此类推,将每张牌经过的索引都记下来,因为每张牌最后是几是知道的,然后反推出1~N张牌是多少,但是发现记录牌经过的索引有点麻烦,效率也不高,记录的数组的第一个元素即为所求。

早上到了公司一边干活,一边实现这个算法,从题目可以很容易看出奇数位一次为1~N/2,剩下的就是求偶数位的值,马上写了个算法,运行是发现有的结果是正确的,大部分是错的,于是写了个测试方法,测试方法就很简单了,这个方法只用模拟翻牌的过程,然后输出的结果为1~N就是正确的,否则就是错误的。经测试发现我的算法思路完全是错误的,但是通过这个测试算法,我发现了能够正确实现这个题目的方法。这个题目其实不是一个递归的过程,而是一个进栈出栈的过程,奇数位进栈,偶数位出栈。我们知道最后的结果,把每张牌当做一个对象,就是进栈出栈都是以引用的方式,翻牌完成后,按顺序将它们的值一次赋值为1~N,那么我们也就知道开始的牌的顺序了,就这么简单,思路就这么简单,实现起来也就很快,于是马上实现了一个粗糙算法,最后用一个Window Form实现了,发给了同事看看,为了让大家能看得清楚,记录了翻牌的过程,当然要记录过程也是很简单的。

代码真的很简单,将每张牌当做一个对象,这样就不用记录牌经过的过程,引用类型吗!创建的对象的个数为N,过程也是线性的,不会有性能问题。

主要代码如下(代码很粗糙,但思路简单清晰,我们知道就是对的),源码下载

 //将牌定义成对象
        public class Card
        { 
            public int Value=0 ;
            public override string ToString()
            {
                return Value.ToString();
            } 
        }

        //测试算法,记录了翻牌过程
        static List<string> TestResult(Card[] arr)
        {
            if (arr == null)
            { 
               throw new Exception("参数异常");
            }
            int len = arr.Length;
            Queue<Card> queue = new Queue<Card>(len);
            foreach (Card i in arr)
            {
                queue.Enqueue(i);
            }
            List<string> list = new List<string>(len);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < len; i++)
            {
                list.Add(GetItem(sb.ToString(),queue));
                Card cur = queue.Dequeue();
                queue.Enqueue(cur);
                sb.Append(queue.Dequeue().ToString().PadRight(3,' ')+"  ");
            }
            return list;
        }

        static string GetItem(string s,Queue<Card> queue)
        { 
            StringBuilder sb = new StringBuilder(s);
            foreach (var item in queue)
            {
                sb.Append(item.ToString().PadRight(3, ' ') + "  ");
            }
            return sb.ToString();
        }

        //实现翻牌的算法
        static Card[] TestArr(int size)
        {
            Card[] arr = new Card[size];
            for (int i = 0; i < size; i++)
            {
                arr[i] = new Card();
            }
            int len = arr.Length;
            Queue<Card> queue = new Queue<Card>(len);
            foreach (Card i in arr)
            {
                queue.Enqueue(i);
            }
            for (int i = 1; i <= len; i++)
            {
                Card cur = queue.Dequeue();
                queue.Enqueue(cur);
                cur = queue.Dequeue();
                cur.Value = i;
            }
            return arr;
        }

几个截图,如果题目我说得不清楚,下面几张图应该可以让大家看得更明白

 

posted @ 2012-09-16 12:16  啊汉  阅读(3760)  评论(9编辑  收藏  举报