UVa OJ 130 - Roman Roulette (罗马轮盘赌)

Time limit: 3.000 seconds
限时:3.000秒

 

Background and Problem
背景与问题

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?
历史学家弗拉维·约瑟夫曾讲述过这样的故事:在公元前67年的罗马犹太战争中,罗马攻下了他掌权的城市。随后,他与另外40个反抗战士一起逃到了一个山洞中而被困在里面。罗马人发现了他的下落并劝他投降,但他的战士们拒绝投降。于是他提议互相杀死对方,一个接一个,顺序由大家决定。有一种传统的方式,即为了公平起见所有人都站成一个环,从某一点开始,循环计数,每隔两个活着的人就杀掉一个。最后只有约瑟夫幸存了下来,投降了罗马。问题来了,约瑟夫如何知道一开始站第31位才会幸存到最后呢?是不是趁着洞穴黑暗大家没注意,悄悄地用41个石子演练过了?还是他用数学方法计算出了这个位置?

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 proceeds from the person to his immediate left, with the kth person being killed, and so on, until only one person remains.
类似于约瑟夫所述的问题,你的程序需要处理如下改编过的问题。最初n(n > 0)个人面朝内围成一个环,并依次按顺时针编号1到n。你的编号为1,第一个轮到的人编号为i,同样顺时针方向开始计数,直到第k(k > 0)个人把他杀掉。然后再从下一个还活着的人开始向后数到第k个人,由这个人来把刚才杀掉的人埋葬,并站到被杀的人的位置上。然后再从他左面第一个还活着的人开始计数,杀掉第k个人,以此类推,直到只有一个人还活着。

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.
比方说,n = 5,k = 2,i = 1,被杀的顺序应为:2、5、3和1。4幸存。

 

Input and Output
输入与输出

Your program must read input lines containing values for n and k (in that order), and 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. Input will be terminated by a line containing values of 0 for n and k.
你的程序须进入多行n和k值(每行n在前,k在后),对应于每行输入你的程序计算并输出开始的人的编号,以保证你自己能幸存到最后。比如,在上面的例子中,从第3个位置开始你就是安全的。n和k都为0时表示输入结束。

Your program may assume a maximum of 100 people taking part in this event.
你的程序可以假设在这个过程中最多有100个人。

 

Sample Input
输入示例

1 1
1 5
0 0

 

Sample Output
输出示例

1
1

 

Analysis
分析

类似于经典的约瑟夫环问题,这类问题都是可以用数学推导求出通式的。但这道题目的过程要远比约瑟夫环问题复杂得多,分析通式比较困难,且由于数据量很小(少于100人),可直接按模拟类型的题目对待求解。

模拟的实现使用动态数组是非常方便的,过程也很简单。数组初始存储每一个人的编号,从第0个元素(1号)开始计数,每次杀死一个人前先不要将这个人的编号删除,而是先找出要来埋他的人,将他们的编号互换,然后将埋他的人原来所在的位置删掉即可。最后计算出的是从1号开始计数,最后能幸存的人编号p,那么从你前面第p个人开始你就是安全的(你站在第1位),即n - p。这个换算的原理是显而易见的。

 

Solution
解答

#include <iostream>
#include <vector>
using namespace std;
int main(void) {
	//循环处理每一行输入的数据
	for (int n, k; cin >> n >> k && n * k != 0; ) {
		vector<int> Circle; //将动态数组中存入顺序的编号,从1起
		for (int i = 0; i < n; Circle.push_back(++i));
		//循环杀人,直到只有一个幸存者。m为被杀死人的编号
		for (int m = (k - 1) % Circle.size(), t; Circle.size() != 1; ) {
			//计算埋葬者在数组中的位置,人数按已杀死m号人后计算
			t = (m - 1 + k) % (Circle.size() - 1);
			//如果t在m的后面,则要加1(数组尺寸在此时并未改变)
			t = (t + (t >= m)) % Circle.size();
			Circle[m] = Circle[t]; //将埋葬者移动到被杀死人的位置上
			Circle.erase(Circle.begin() + t); //删除原埋葬者的位置
			//继续向后数k个人,做下一次循环
			m = (m - (t < m) + k) % Circle.size();
		} //换算后输出结果
		cout << (n - Circle.front() + 1) % n + 1 << endl;
	}
	return 0;
}

转载于:https://www.cnblogs.com/devymex/archive/2010/09/04/1813034.html

杭州电子科技大学在线评测系统(杭电OJ)中的题目1000-1100是一系列编程题,我将分别进行回答。 1000题是一个简单的入门题,要求计算两个整数的和。我们可以使用一个简单的算法,读取输入的两个整数,然后将它们相加,最后输出结果即可。 1001题是一个稍微复杂一些的题目,要求实现字符串的逆序输出。我们可以使用一个循环来逐个读取输入的字符,然后将这些字符存储在一个数组中。最后,我们可以倒序遍历数组并将字符依次输出,实现字符串的逆序输出。 1002题是一个求最大公约数的问题。我们可以使用辗转相除法来解决,即先求出两个数的余数,然后将被除数更新为除数,将除数更新为余数,直至两个数的余数为0。最后的被除数就是最大公约数。 1003题是一个比较简单的排序问题。我们可以使用冒泡排序算法来解决,即每次比较相邻的两个元素,如果它们的顺序错误就交换它们的位置。重复这个过程直至整个数组有序。 1100题是一个动态规划问题,要求计算给定序列中的最长上升子序列的长度。我们可以使用一个数组dp来保存到达每个位置的最长上升子序列的长度。每当遍历到一个位置时,我们可以将其和之前的位置比较,如果比之前位置的值大,则将其更新为之前位置的值加1,最后返回dp数组的最大值即可。 以上是对杭电OJ1000-1100题目的简要回答,涉及了一些基本的编程知识和算法思想。希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值