HDU 5643 King's Game

来源:互联网 发布:淘宝一件代发好做吗 编辑:程序博客网 时间:2024/05/25 01:34

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5643


题意:

有n个人按顺序逆时针围成一个圈1,2,3,...,n。一轮第一个人从 1 开始报数,报到 1 就停止且报到1的这个人出局。

第二轮从上一轮出局的人的下一个人开始从1报数,报到2就停止且报到2的这个人出局。

第三轮从上一轮出局的人的下一个人开始从1报数,报到3就停止且报到3的这个人出局。

第 n - 1轮从上一轮出局的人的下一个人开始从 1报数,报到 n - 1就停止且报到 n - 1的这个人出局。

最后剩余的人是幸存者,请问这个人的标号是多少?


思路:

我们来考虑简单版的约瑟夫环问题,n个人站成圈,报到k的人出去,最后剩下的人的编号。

它的程序可以这么写。

int cal(int n,int k){    int ans = 0;    for(int i = 2; i <= n; i++ ) ans = ( ans + k ) % i;     return ans + 1; //设定编号为0~n-1 所以最后要+1恢复}


这是一个倒推的计算过程。

有n个人编号0~n-1 那么下一轮开始的时候就是当前编号k的人第一个开始报数,我们将其重新编一下号,发现:

n个人的情况:
(k-1)淘汰  k(下一轮开始报数)

n-1个人的情况:
0(开始报数,同时也是上一轮编号为k的位置)

每一轮出去一个人,我们就将所有人重新编号,第一个开始报数的人就编为0,所以上一把编号为k的人,这一把编号为0,以此类推,那么这一把编号为x的人,上一把编号就为(x+k)%i,i是上一把的总人数。既然如此,最后一把剩余的人的编号一定为0,那么我们往前推n-1把就可以得到初始时(有n个人)他的编号是多少。

同理,我们只需要修改上面代码的一个地方就可以了,每一把出去的人报到的数不同,而且是一个倒推过程,所以最后一次报n-1,倒数第二次报n-2...


#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <cstdlib>#include <iostream>#include <algorithm>#include <stack>#include <map>#include <set>#include <vector>#include <sstream>#include <queue>#include <utility>using namespace std;#define rep(i,j,k) for (int i=j;i<=k;i++)#define Rrep(i,j,k) for (int i=j;i>=k;i--)#define Clean(x,y) memset(x,y,sizeof(x))#define LL long long#define ULL unsigned long long#define inf 0x7fffffff#define mod %100000007int T,n;int ans[5009];int cal(int x){    int ans = 0;    rep(i,2,x) ans = (ans+x+1-i) % i;    return ans+1;}void init(){    ans[1] = 1;    ans[2] = 2;    //rep(i,1,15) cout<<i<<" : "<<cal(i)<<endl;    rep(i,3,5000) ans[i] = cal(i);}int main(){    init();    cin>>T;    while(T--)    {        scanf("%d",&n);        printf("%d\n",ans[n]);    }    return 0;}



2 0