P1145 约瑟夫

题目链接:

考虑使用循环链表来维护该环形数据结构。但下列代码只能通过70/100

image

#include <bits/stdc++.h>

using namespace std;

int ne[50];
int main()
{
	int k, n, m;
	bool flag = true;
	cin >> k;
	
	n = 2 * k, m = k + 1;
	for (int i = 1; i < n; i++) ne[i] = i + 1;
	ne[n] = 1;
	
	int p = n, t = k;

	for (; ; m++) {
		flag = true;
		p = n, t = k;
		for (int i = 1; i < n; i++) ne[i] = i + 1;
		ne[n] = 1;
		while (t--) {
			for (int i = 1; i < m; i++) p = ne[p];
			
			if (ne[p] >= 1 && ne[p] <= k) {
				flag = false;
				break;
			}
			else ne[p] = ne[ne[p]];
		}

		if (!flag) continue;
		else {
			cout << m;
			return 0;
		}
	}
	return 0;
}

经过思考,当\(\rm m\)的值较大(远远超过剩余总人数)的时候,寻找\(\rm p\)的过程有大量重复。事实上,只需走\(\rm m\)%\(\rm sum\)步(其中\(\rm sum\)为剩余总人数)即可,重复了好几圈。
值得注意的是,当\(\rm m\)%\(\rm sum\)\(0\)的时候,for循环中的for (int i = 1; i < m%sum; i++)这一步会不执行,且发现规律是删除自己的这个元素。因此需要用到节点的前驱节点。考虑改用双向循环链表维护。

易错点:1、在每次删除完节点之后,要修改某些节点的ne[]和pre[]的值(pre不能忘)
2、m++之后进入到一轮新的循环,链表的结构需要重新定义,指针p的初始指向,sum等变量的值要和初始时一致。

以下是AC代码。

#include <bits/stdc++.h>

using namespace std;

int ne[150], pre[150];

int main()
{
	int k, n, m, sum;
	bool flag = true;
	cin >> k;
	
	n = 2 * k, m = k + 1, sum = 2 * k;
	for (int i = 1; i < n; i++) ne[i] = i + 1;
	ne[n] = 1;
	
	int p = n, t = k;
	
	for (; ; m++) {
		flag = true;
		p = n, t = k, sum = n;
		
		for (int i = 1; i < n; i++) ne[i] = i + 1;
		ne[n] = 1;
		pre[1] = n;
		for (int i = 2; i <= n; i++) pre[i] = i - 1;
		
		while (t--) {
			int y = m % sum;
			if (y == 0) p = pre[p];	
			else for (int i = 1; i < y; i++) p = ne[p];	
			if (ne[p] >= 1 && ne[p] <= k) {
				flag = false;
				break;
			}
			else {
				ne[p] = ne[ne[p]];
				pre[ne[p]] = p;
				sum--;
			}
		}

		if (!flag) continue;
		else {
			cout << m;
			return 0;
		}
	}
	return 0;
}
posted @ 2024-01-14 23:44  胖柚の工作室  阅读(46)  评论(0)    收藏  举报