您的位置:首页 > 其它

约瑟夫问题例题小结

2014-11-06 23:12 281 查看
类型1:约瑟夫问题原题:(http://acm.hdu.edu.cn/showproblem.php?pid=2925)

  大体就是一圈人,数到m的退出,询问最后留下来的人。

  这样的题是约瑟夫系列问题的基础,普通暴力做法O(n*m),不够优秀,可以通过数学方法优化至O(n),其所用的思路是将一个规模n的问题转移成规模n-1的问题。

    公式形式推导都很简单:f(i)表示规模i的约瑟夫问题最后留下人的编号,f(i)=(f(i-1)+m)%i

类型2:规模变态版:(http://poj.org/problem?id=1781)

  类型1限制只数1,2,即每次去掉偶数项,易推导f(i)=(f(i-1)+2)%i

  然而数据时限卡O(n),通过《具体数学》上一种及其奇葩的推导,有一个O(log)解法,即f(i)=i循环右移(把二进制最高为的1放在最右面)

类型3:类型1变式:(http://poj.org/problem?id=1012)

  2×n个人,通过约瑟夫方式间隔m杀人,使后n个坏人在前n个好人之前被杀的最小m。

  引入Joseph递推公式,设有n个人,(0~n-1),间隔m, f(i) 表示当前子序列中第i轮要退出的那个人

    则 f(0)=0;

      f(i)=(f(i-1)+m-1)%(n-i+1);

  这个公式我真不知道是咋来的。。。

  反正假设我们知道这个公式,那么就可以从小大到枚举m,O(n)枚举。

类型4:模型转变

  假设不再是圈,而是一个链,每次从没有死的第一个开始计数。

  这道题我确实是结合标程与打表找规律做出来的。

  打表每一轮死亡的人,设f(i,j)表示第i轮第j个死的人,我们可以将f(i,j)按照不同j值分类,这些都比较好想,

    这道题难点在于第a个人所表示的f(i,j)=a,则f(i-1,j)=a-a/k-1,人从0计数。

  上面做完就随便怎么都能做了。

现在暂时总结这么多,总之约瑟夫问题足够神奇,也足够恶心。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 99000000
const int ten[7]={1,10,100,1000,10000,100000,1000000};

int main()
{
freopen("input.txt","r",stdin);
char str[5];
int n=0;
int x;
while (scanf("%s",str),str[0]+str[1]+str[3]-'0'*3)
{
x=((str[0]-'0')*10+str[1]-'0')*ten[str[3]-'0'];
for (int i=30;i>=0;i--)
{
if (x&(1<<i))
{
printf("%d\n",(((x^(1<<i))<<1)+1));
break;
}
}
}
}


poj 1781 O(log)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: