UVa12657移动盒子

当插入删除搬家移动位置比较多的时候,普通数组必定超时,此时在数据结构上考虑优化,用链表。

要知道盒子的前驱和后继,采用双向链表。

结点类三个成员,左指针、右指针、数据值。指针是数组下标。

因为该题目是根据数据值来移动,而不是数据值的位置,所以最好是把数据值就当作数组下标,直接将元素对应到房间下标。因此结点类就不需要3个成员,只需要2个。

此外,仍然用0号代表头结点,初始化first指针指向0号,头结点的右指针指向第一个实际元素,头结点的左指针指向最后一个元素。维护last指针指向最后一个元素,最后一个元素的左指针指向前驱,右指针指向头结点。

操作3——交换位置

画个图就知道如何写了,而且还要确定好执行顺序。

操作4——反转整条链

反转整条链,将last指针和头结点指针对调。但是这样还不够,2、3操作涉及把盒子移动到左边右边,如果反转了,左边右边关系就改变了,如果对每个结点对调左右指针会比较麻烦和费时。因此引入一个标记,一旦引入这个标记,其他操作都要考虑这个标记。线段树的懒标记也是引入标记。

提示6-6非常核心:

如果数据结构上的某一个操作很耗时,有时可以用加标记的方式处理,而不需要真的执行那个操作。但同时,该数据结构的所有其他操作都要考虑这个标记。

如果a在b左边,反转整条链,a就在b的右边。如果a要放到b的左边,反转整条链就是把a放到b的右边。

例子:1 a b

  • 没有反转 执行前 a <-> c <-> d <-> b 执行后 c <-> d <-> a <-> b
  • 有反转 执行前 b <-> d <-> c <-> a 执行后 a <-> b <-> d <-> c

? 再把这个执行结果反转回去就是c <-> d <-> b <-> a,相当于没有反转 2 a b

? 因此如果有反转,输出的时候也要反转。

求奇数位置的盒子编号就挨着走链,直到指回头结点。

如果是盒子总数是奇数,反转就不影响结果,如果盒子总数是偶数,反转就是取补。注意最大50亿超了int。注意修改数据类型则所有地方,包括输入输出都要改。涉及long long就要随时注意类型转换,还有防止溢出的问题。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 100010;

int llink[MAXN];
int rlink[MAXN];
int first, last;
int inv; // 反转标记 
int n, m;
void Init(int n) {
	inv = 0;
	first = 0;
	last = n;
	for(int i = 1; i < MAXN - 1; i++) { // 从1开始没有给0弄到 
		llink[i] = i - 1;
		rlink[i] = i + 1;
	}
	rlink[0] = 1; 
	rlink[n] = 0;
	return;
}

void SWAP(int a, int b) { // 交换位置 
	rlink[llink[a]] = b;
	llink[rlink[a]] = b;
	
	rlink[llink[b]] = a; 
	llink[rlink[b]] = a;
	
	int t = rlink[b];
	rlink[b] = rlink[a];
	rlink[a] = t;
	 
	t = llink[b];
	llink[b] = llink[a];
	llink[a] = t;	
	return;
}

void InsertToLeft(int a, int b) { // 将a移动到b左边 
	rlink[llink[a]] = rlink[a];
	llink[rlink[a]] = llink[a];
	
	rlink[llink[b]] = a;
	
	llink[a] = llink[b];
	rlink[a] = b;
	llink[b] = a;
	
	return;
}

void InsertToRight(int a, int b) { // 将a移动到b右边
	rlink[llink[a]] = rlink[a];
	llink[rlink[a]] = llink[a];
	
	llink[rlink[b]] = a;
	
	rlink[a] = rlink[b];
	llink[a] = b;
	rlink[b] = a;
	
	return;
}

int main() {
	int k = 0;
	while (scanf("%d%d", &n, &m) == 2) {
		Init(n);
		for (int i = 0; i < m; i++) {
			int op = 0;
			scanf("%d", &op);
			if (op == 4) { // 反转整条链 
				if (inv == 1) {
					inv = 0;
				} else {
					inv = 1;
				}
			} else if (op == 3) { // 交换位置 
				int a, b;
				scanf("%d%d", &a, &b);				
				SWAP(a, b);
			} else { // 移动到左边或者右边 
				if (inv == 1) {
					op = 3 - op;
				}
				int a, b;
				scanf("%d%d", &a, &b);
				if (op == 1) { // 将a移动到b左边 
					if (llink[b] == a) {
						continue;
					}
					InsertToLeft(a, b); 
				} else {
					if (rlink[b] == a) {
						continue;
					}
					InsertToRight(a, b); // 将a移动到b右边 
				}
			}
		}
		long long ans = 0; // 最大的50亿超了int 
		int cur = rlink[0]; // 指向第一个元素 
		int i;
		for (i = 1; cur != 0; i++) {
			if (i % 2 == 1) {
				ans += cur;
			}
			cur = rlink[cur];
		}
		if (inv == 1 && n % 2 == 0) {
			ans = (long long)(n + 1) * (n / 2) - ans; 
		}
		printf("Case %d: %lld\n", ++k, ans);
	}
	return 0;
}

UVa12657移动盒子

上一篇:【Android】Toast on non-UI thread


下一篇:无法加载文件 C:\Users\admin\AppData\Roaming\npm\anyproxy.ps1