UVA130 HDU1628 Roman Roulette【约瑟夫环】

The historian Flavius Josephus relates how, in the Romano-Jewish conflict of 67 A.D., the Romans took the town of Jotapata which he was commanding. Escaping, Jospehus found himself trapped in a cave with 40 companions. The Romans discovered his whereabouts and invited him to surrender, but his companions refused to allow him to do so. He therefore suggested that they kill each other, one by one, the order to be decided by lot. Tradition has it that the means for effecting the lot was to stand in a circle, and, beginning at some point, count round, every third person being killed in turn. The sole survivor of this process was Josephus, who then surrendered to the Romans. Which begs the question: had Josephus previously practised quietly with 41 stones in a dark corner, or had he calculated mathematically that he should adopt the 31st position in order to survive?
    Having read an account of this gruesome event you become obsessed with the fear that you will find yourself in a similar situation at some time in the future. In order to prepare yourself for such an eventuality you decide to write a program to run on your hand-held PC which will determine the position that the counting process should start in order to ensure that you will be the sole survivor.
    In particular, your program should be able to handle the following variation of the processes described by Josephus. n > 0 people are initially arranged in a circle, facing inwards, and numbered from 1 to n. The numbering from 1 to n proceeds consecutively in a clockwise direction. Your allocated number is 1. Starting with person number i, counting starts in a clockwise direction, until we get to person number k (k > 0), who is promptly killed. We then proceed to count a further k people in a clockwise direction, starting with the person immediately to the left of the victim. The person number k so selected has the job of burying the victim, and then returning to the position in the circle that the victim had previously occupied. Counting then proceeeds from the person to his immediate left, with the k-th person being killed, and so on, until only one person remains.
    For example, when n = 5, and k = 2, and i = 1, the order of execution is 2, 5, 3, and 1. The survivor is 4.
Input
Your program must read input lines containing values for n and k (in that order. Input will be terminated by a line containing values of ‘0’ for n and k.
    Your program may assume a maximum of 100 people taking part in this event.
Output
For each input line output the number of the person with which the counting should begin in order to ensure that you are the sole survivor. For example, in the above case the safe starting position is 3.
Sample Input
5 2
1 5
0 0
Sample Output
3
1

问题链接UVA130 HDU1628 Roman Roulette
问题简述:(略)
问题分析
    约瑟夫环问题。N个人排成一圈,按顺时针从1到N编号。从1开始顺时针数到第k个人,让其出局,接着数到第k个人,让他站在出局者的位置上。然后从出局者的后一个人位置开始,重复上述过程直到环中只剩1个人。求剩下的人是1号时,最初应该从什么位置开始数起。
    模除运算是解决围成一圈问题的好方法。
程序说明
    给出2种解法程序,一种用STL的vector实现,稍微有点复杂,还需要考虑只剩下2个人的特殊处理。另外一种是用数组来模拟,也许效率更高一些。
参考链接:(略)
题记:(略)

AC的C++语言程序如下:

/* UVA130 HDU1628 Roman Roulette */

#include <bits/stdc++.h>

using namespace std;

const int N = 100;
int a[N];

// 数k个人
int find(int s, int k, int n)
{
    while (k)
        if (a[s = (s + 1) %n ] != -1)
            k--;
    return s;
}

int main()
{
    int n, k;
    while(~scanf("%d%d", &n, &k) && (n || k)) {
        // 初始化:将1 - n映射为0 - n-1进行计算
        for(int i = 0; i < n; i++)
            a[i] = i;

        // 模拟:移除n-1人
        int p = (k - 1) % n;
        for(int i = 1; i < n; i++) {
            a[p] = -1;
            swap(a[find(p, k, n)], a[p]);
            p = find(p, k, n);
        }

        printf("%d\n", (n - a[p]) % n + 1);
    }

    return 0;
}

AC的C++语言程序如下:

/* UVA130 HDU1628 Roman Roulette */

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int n, k;
    while(~scanf("%d%d", &n, &k) && (n || k)) {
        // 初始化:将1 - n映射为0 - n-1进行计算
        vector<int> v;
        for(int i = 0; i < n; i++)
            v.push_back(i);

        // 模拟
        int p = -1, nextp, i;
        while(v.size() > 1) {
            p = (p + k) % v.size();
            if(v.size() == 2)
                v.erase(v.begin() + p);
            else {
                for(i = 1, nextp = p; i <= k; i++) {
                    nextp = (nextp + 1) % v.size();
                    if(nextp == p) i--;     // 跳过移除的
                }
                swap(v[p], v[nextp]);
                v.erase(v.begin() + nextp);
                if(p > nextp) p--;
            }
        }

        printf("%d\n", (n - v[0]) % n + 1);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/tigerisland45/article/details/89735660