hdu -- 1443 Joseph

来源:互联网 发布:数据线上带充电宝 编辑:程序博客网 时间:2024/05/21 10:22
     约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称“丢手绢问题”)
    据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。    


   在hdu中,这道题要先杀掉后排才能杀掉前排,且杀掉一个人后,重新回环,即题目为杀掉后排之前不得杀掉前排,于是如下:
   1.将死之人为 die = m % i == 0 ?i :m % i;(i是剩余的人数)
   2.不可杀的范围:!(a[die] =< k && a[die] >= 1)不为前k个人(前面给数组赋了初值)

   3.移位

借助a[ 0 ]进行移位,先将最后一位移给a[ 0 ],再将数组整体后移移位,直到a [ 0 ] ==  b ; (b == a[die]).


   代码如下 

#include<stdio.h>int main(){freopen("input.txt","r",stdin);freopen("output.txt","w",stdout);int a[25],k,m,die,kill;while(scanf("%d",&k) != EOF){for(m = k + 1;;m++){//给数组赋初值for(int i = 1; i <= 2*k; i++)a[i] = i;kill = 0; //已杀人数for(int i = 2 * k;;i--){die = m % i == 0 ? i : m % i;if(!(a[die] >= 1 && a[die] <= k)) //条件判断{int b = a[die];kill++;a[0] = a[i]; while(a[0] != b)  //移位{for(int j = i; j > 0; j--)a[j] = a[j - 1];a[0] = a[i];}}else  break;}if(kill == k) { printf("%d,",m); break;}}}return 0;}

   这时打出的代码在k = 14 时候,将近需要11s,这严重超时啊有木有~~~~~

   这时考虑到约瑟夫问题数据少,所以可以用打表法来过题,所谓打表法,先写代码将数据打出,然后初始化于数组中,这里推荐使用输入流和输出流,我使用了freopen(“input.txt”,“r”,stdin);

freopen(“output.txt”,“w”,stdout);将这两代码,写于main函数入口处,之后的scanf只会从文件input.txt读入数据,所以要自己新建,一般建于与cpp相同的目录下,也可以建于其它地方,但要写绝对路径或相对路径(默认是与cpp相同的目录),例如:freopen(“D://hdu/joseph/input.txt”,"r",stdin);你的printf将把数据写于output.txt中(不必自己新建),这样,你就可以简单的将数据复制给数组打表了。

    打表代码:

#include<stdio.h>int a[14] = {2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881,13482720};int main(){    int b;    while(scanf("%d",&b) == 1 && b != 0)    {        printf("%d\n",a[b-1]);    }    return 0;}