Chinaunix首页 | 论坛 | 博客
  • 博客访问: 102585
  • 博文数量: 52
  • 博客积分: 2095
  • 博客等级: 大尉
  • 技术积分: 500
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-08 13:29
文章分类

全部博文(52)

文章存档

2010年(1)

2009年(24)

2008年(27)

我的朋友

分类:

2009-07-27 20:01:58

题目:n个人(1到n)报数,从1报到r,报r的出局,然后下一个又从1开始报。。。,问最后留下的人是几号。

用循环链表应该思路最清晰,报r的人就删除此节点。用数组的话,看见过解法,要判断该位置是否有人,还要注意取余,因为是循环的一个圈。我用数组解就是这样,初始化后,开始报数,用一个变量记录当前报数的值,只有位置上有人(非0)时才增加,到r时把该位置置0。这样操作一次出局一人,循环n-1次就剩 最后一人了,也可以再用个数组记录每次出局的人,就更加清晰。

void josh(int n,int r) //n个人,报r的出局

{
    int a[1000];
    int i,sum,now; //i对r计数,sum表示还没出局的人,now表示当前“指针"不管位置上有人与否-

    int j;
    for(j=1;j<=n;j++) //初始化数组,从a[1]开始

        a[j]=j;
    sum=n;
    now=1;
    for(j=1;j<n;j++){ //循环n-1次,最后剩下的一个为结果

        i=0;
        while(i<r) {
            if (a[now]==0) {
                ++now; //当前位置是0,已经出局了,就只移动指针

                if (now>n)
                    now=1;
                //continue;

            }
            else { //此位置还有人

                i++; //i计数,表示此人报数了

                if(i==r)
                    a[now]=0;
                ++now;
                if (now>n)
                    now=1;
            }
        }
        --sum;
    }

    for(j=1;j<=n;j++)
        if (a[j]!=0)
            printf("result is:%d",a[j]);
}

然后发现网上有用递推实现,时间复杂度是O(n)。想想也是,应该是有规律的。参考了http://hi.baidu.com/wind__talker/blog/item/2375c6238d207547925807fb.html等地方。然后在r=3的时候枚举了一下,还是很明显的,就是不好用公式表达。我上面的程序是从1开始计数,没有用a[0],分析的时候也是这样,和参考的地方有点不同,结果上来看基本一致。
第一次出列的是r mod n 号,取余是怕r比n大。这里有个问题,我是从1到n号,r mod n等于0怎么办,没有0号啊,参考资料里面是从0开始,刚好避免了这个问题。。。我这里没有考虑,最后按答案来修正的了。
继续,第一次出列的是r mod n 号,一般也就是r号嘛,先当做r吧;然后剩下的n-1个数等价于n-1规模的子问题了,不过号码不是从1开始,而是r+1、r+2、...、0、1、...r-1。如果已经知道n-1的问题的答案f(n-1)=k,则在上面的号码中数到k,对应的就是f(n)也就是答案了。可以摸索验证一下,递推式是:
f(n)=[r mod n+f(n-1)] mod n
里面的取余其实可以不要,等价于
f(n)=[r +f(n-1)] mod n
以为很完美,和参考资料一样,可是就因为人数没有从0开始计数到n-1(我这不是更自然嘛,都从1数啊),f(n)可能为0,而没有0号,当为0号的时候答案是n。应该是对的吧。总之基本上是解决了,水平就只有这样了。

看到稍微加难的问题,第一次报数不是从第一个人开始,而是从第s号开始报,答案又是什么呢?取巧的话,可以第一次就先把数组从s开始初始化。。。
阅读(679) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~