约瑟夫问题(问题总结)

约瑟夫问题描述

n 个人标号0,1,2,…n-1 。逆时针站一圈,从 0 号开始,每一次从当前的人逆时针数 个,然后让这个人出局。问最后剩下的人是谁。

线性解法模板

根据问题的描述,很容易想到递推公式Josephus(n,k)=Josephus(n-1,k)。(其中n是规模,k是循环次数)。
如此有一下代码:

int josephus(int n, int k) {
  int res = 0;
  for (int i = 1; i <= n; ++i) res = (res + k) % i;
  return res;
}

遇到的小问题

1.编号是从1-n的问题

也就是说是把所有的人按照逆时针的顺序从1编号到n。而不是从0开始。
对于这种问题,由于问题的实质并没有改变,只是将编号加1,所以先根据原来的模板求出结果,在加一转换即可
代码如下:

int josephus(int n, int k) {
  int res = 0;
  for (int i = 1; i <= n; ++i) res = (res + k) % i;
  return res + 1;
}

2.不从1开始报数的问题

解法和上一个类似,只不过编号的转换公式改变了。具体见代码:

int w;   //编号为1-n,从w开始报数
int josephus(int n, int k) {
  int res = 0;
  for (int i = 1; i <= n; ++i) res = (res + k) % i;
  return (res + w) % n;
}

直接模拟的问题

见POJ3750 小孩报数问题
题目不难,只是模拟的时候在报数的模拟过程中,由于第一个报数的小孩编号为1,所以要从他的前一位开始记录。

//poj 3750 小孩报数问题
#include
#include
#include

using namespace std;
vector<string> p;
int n, w, s;
bool vis[70];

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int i, j;
	char ch;
	string str;
	int cnt = 0;
	cin >> n;
	for (i = 0; i < n; ++i)
	{
		cin >> str;
		p.push_back(str);
	}
	cin >> w >> ch >> s;
	cnt = 0;
	if (w >= 2)i = w - 2;
	else i = w + n - 2;  //需要从w-1的前一号开始数
	while (cnt < n)
	{
		j = 0;
		while (j < s)
		{
			i = (i + 1) % n;
			if (!vis[i])
				j++;
		}
		cout << p[i] << endl;
		vis[i] = true;
		cnt++;
	}
	return 0;
}

你可能感兴趣的:(约瑟夫问题)