数论 约瑟夫环
当数据比较小时,可以直接模拟暴力:
/*t=n;//模拟递推法,超时
i=m;j=0;book[m]=1;t--;
while(t!=1)
{
j++;i++;
while(1)
{
if(i==n+1)
i=1;
if(book[i]==1)
i++;
else
break;
}
if(j==k)
{
j=0;
book[i]=1;
t--;
}
}
for(i=1;i<=n;i++)
{
if(book[i]==0)
{
printf("%d\n",i);
break;
}
}*/
但当数据过大时,模拟法会超时,所以通过数学的推导,有以下公式:
运用dp思想,n=2时的值可以通过n=1递归出来:
约瑟夫环数学公式:设n个人,第k个移除;
f(n)=(f(n-1)+k)%n;这是一个递推公式。
约瑟夫环:一般就是从一个人的时候(胜利者的下标是0)开始推到第n个人的时候,胜利者的下标是什么。
f(1) = 0;
f(i) = (f(i-1)+k)%i; // i表示当前环 人的人数,k表示每k个人杀。
————————————————————————————————————————
例题一代码:
#include<stdio.h>
#include<math.h>
#include<iostream>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<string.h>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int book[10010];
int main()
{
int i,j,z,n,m,k,t,l;
while(~scanf("%d%d%d",&n,&k,&m))
{
if(n==0&&k==0&&m==0)
break;
memset(book,0,sizeof(book));
/*t=n;//模拟递推法,超时
i=m;j=0;book[m]=1;t--;
while(t!=1)
{
j++;i++;
while(1)
{
if(i==n+1)
i=1;
if(book[i]==1)
i++;
else
break;
}
if(j==k)
{
j=0;
book[i]=1;
t--;
}
}
for(i=1;i<=n;i++)
{
if(book[i]==0)
{
printf("%d\n",i);
break;
}
}*/
book[1]=0;
for(i=2;i<=n;i++)
{
book[i]=(book[i-1]+k)%i;
}
book[n]=(book[n-1]+m)%n;//因为第一个直接删除的是第m个人,没有走流程,所以当i=n时,把k换成m
printf("%d\n",book[n]+1);
}
return 0;
}
/*
约瑟夫环数学公式:设n个人,第k个移除;
f(n)=(f(n-1)+k)%n;这是一个递推公式
*/
————————————————————————————————————————
例题二代码:
/*
要求:城市2是最后一个选择的,从而找出符合该条件的m。
并且第一个城市总是第一个选择,可把问题转化为n-1个城市,第二个城市的序号为1.
根据递归公式,也就是book(n)=0即符合条件
*/
#include<stdio.h>
#include<math.h>
#include<iostream>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<string.h>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int book[10010];
int main()
{
int i,j,z,n,m,k,t,l;
while(~scanf("%d",&n))
{
if(n==0)
break;
for(m=2;;m++)
{
book[1]=0;
for(i=2;i<n;i++)
{
book[i]=(book[i-1]+m)%i;
}
if(book[n-1]==0)
{
printf("%d\n",m);
break;
}
}
}
return 0;
}
/*
约瑟夫环数学公式:设n个人,第k个移除;
f(n)=(f(n-1)+k)%n;这是一个递推公式
*/