這道題是約瑟夫問題的變形,大意就是給定很多人,圍成一個環,從一開始數,每次殺死第三人,事實上按數據是每數到2就殺死當前的人。第一個樣例中,一共五人,先殺死2號,之後是4號,然後1號,最後殺死5號,於是3號最後活下來了。
輸入數據,讀爲字符型較方便,我是直接讀一個字符串s,然後計算人數人數,這個人數很多,模擬的話一定會超時,但是模擬的方法先簡述一下:
設有n個人,數到m殺死當前人。
定義一個布爾型數組記錄人的生死,初始化爲true,代表都活着。定義一個k=0,記錄已經殺死幾個人。一個變量t記錄當前人的編號,x記錄數到了幾。
當k不等於n時,t每次加一,若t大於n;則令t=1。若通過布爾型數組判斷編號爲t的人還活着,x也加一。若x==m,令數組下標爲t的值爲false。那麼最後的t值即爲活下來的人。
核心代碼(模擬):
memset (a,true,sizeof(a));
s=0,t=0,k=0;
do
{
t++;
if (t>n)
t=1;
if (a[t]==true)
s++;
if (s==m)
{
a[t]=false;
s=0;
k++;
if(k==n)
cout<<t<<endl;
}
}while (k!=n);
然而對於這道題,需要找到一些規律,否則會超時。
那麼假設n個人,每次殺死第m人時活下來的是編號爲k的人,當m一定,對於n+1個人來說,第一個殺死的是編號爲m的人,還剩下n人,那麼這就和n個人的情況一樣了,只是不是從第一個人開始數,而是從m+1開始。這樣有n+1個人時,最終活下來的爲第k+m號人。同理,n+2時,爲k+2m號。
問題是k每次加m時,n只是加上1,可能會出現算出的活下來的人的編號大於n的情況,此時怎麼辦呢?顯然,我們對算出的數對n取模即可。
這時對本題中m一直爲2,對於n爲2的整數次冪時,第一次轉一圈殺死了所有的編號爲偶數的人,然後又從1號開始,這時剩下n/2個人,人數還是2的整數次冪,不妨把這些人重新按順序編號,又殺死了那些編號爲偶數的一半人,依然回到1……最後顯然回到n=2的情況,最終活下來的人爲1號。所以,當n爲2的整數次冪時,活下來的人是1號。
這樣對於一個任意的n,若是2的整數次冪答案爲1,。其他的只要找到不大於它的最大的2的整數次冪的數x,然後算出1+2(n-x)即爲答案。那麼關於這個值爲什麼必然小於下一個2的整數次冪數,下一個數爲2x,n-x>x時,n>2x,不大於n的最大的2的整數次冪的數就是2x了。
AC代碼
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
long long p;
int n,m,z;
string s;
long long ef[50]={0,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456};
//數組打表記錄2的整數次冪
int main()
{
while (cin>>s,s[0]-'0'+s[1]-'0')
{
n=s[0]-'0';
m=s[1]-'0';
z=s[3]-'0';
p=(n*10+m);
for (int i=1;i<=z;i++)
p*=10;
if (p==1||p==2||p==4)
{
cout<<1<<endl;
}
else
{
if (p==3)
cout<<3<<endl;
else
{
long long t;
int k;
for (int i=1;p>ef[i];i++)
k=i;
if (ef[k+1]==p)
cout<<1<<endl;
else
{
t=1+2*(p-ef[k]);
cout<<t<<endl;
}
}
}
}
return 0;
}