菜鸟就要老老实实重新学起:
约瑟夫环:
就是长度为n的环每数m个就删去,最后留下一个。
一般问题有两种,求某种m情况下最后留下的序号
核心就是循环倒推:
for(i=2;i<=n;i++)
res=(res+m)%i;
或者求一种m使得某一段序列最后被留下。
核心就是枚举m正向循环,同时取模求当前删去数的序号:
res=0;k=1;
for(i=0;i<n;i++)
{
res=(res+k-1)%(2*n-i);
if(res<n)
i=-1,res=0,k++;
}
eg:
POJ3517 And Then There Was One
http://poj.org/problem?id=3517
题意:
简单约瑟夫环,求n长环先数m个去掉,然后每一轮数k个去掉,最后剩下的序号。思路:
easycode:
int main()
{
int i,j,k,kk,t,x,y,z,res,n,m;
while(scanf("%d%d%d",&n,&k,&m)!=EOF&&n)
{
for(i=2,res=0;i<n;i++)
res=(res+k)%i;
res=(res+m)%n;
printf("%d\n",res+1);
}
return 0;
}
POJ1781 In Danger
http://poj.org/problem?id=1781
题意:
就是步长为2的约瑟夫环。
思路:
直接O(n)循环会t,但是确定了步长通过打表发现了规律,是长度为2的指数的奇数循环。
直接去掉二进制的第一位,找到循环长度得出结果。
code:
int main()
{
int i,j,k,kk,t,x,y,z;
while(scanf("%de%d",&n,&m)!=EOF&&n)
{
i=1;j=0;k=2;res=1;
while(m--)
n*=10;
while(n>=i)i<<=1,j++;
n-=(1<<(j-1));
while(n--)res+=2;
printf("%d\n",res);
}
return 0;
}
POJ1021 Joseph
http://poj.org/problem?id=1012
题意:
求长度为n的约瑟夫环用什么步长可以留下前一半数字。
思路:
数据只到14,直接枚举m即可。code:int main()
{
int i,j,k,kk,t,x,y,z;
int a[20];
memset(a,0,sizeof(a));
while(scanf("%d",&n)!=EOF&&n)
{
res=0;k=1;
if(a[n])
{
printf("%d\n",a[n]);
continue;
}
for(i=0;i<n;i++)
{
res=(res+k-1)%(2*n-i);
if(res<n)
i=-1,res=0,k++;
}
a[n]=k;
printf("%d\n",k);
}
return 0;
}
int main()
{
int i,j,k,kk,t,x,y,z;
int a[20];
memset(a,0,sizeof(a));
while(scanf("%d",&n)!=EOF&&n)
{
res=0;k=1;
if(a[n])
{
printf("%d\n",a[n]);
continue;
}
for(i=0;i<n;i++)
{
res=(res+k-1)%(2*n-i);
if(res<n)
i=-1,res=0,k++;
}
a[n]=k;
printf("%d\n",k);
}
return 0;
}
POJ2244 Eeny Meeny Moo
http://poj.org/problem?id=2244
题意:
首先删去第一个,然后求在什么步长时可以最后留下第二个数。
思路:
可以看作总长n-1最后留下第一个,枚举m。code:
int main()
{
int i,j,k,kk,t,x,y,z;
int a[200];
memset(a,0,sizeof(a));
while(scanf("%d",&n)!=EOF&&n)
{
res=0;k=1;
n--;
if(a[n])
{
printf("%d\n",a[n]);
continue;
}
for(i=0;i<n-1;i++)
{
res=(res+k-1)%(n-i);
if(res==0)
i=-1,res=0,k++;
}
a[n]=k;
printf("%d\n",k);
}
return 0;
}