算法竞赛入门经典 例题6-5

UVa12657

Boxes in a Line

将顺序编号的盒子排成一列,模拟处理下面四种指令:

  • 1 X Y,将X挪到Y的左边
  • 2 X Y,将X挪到Y的右边
  • 3 X Y,交换XY
  • 4,反转整个序列

由于涉及在线性序列中间位置的插入和删除,因此还是应该使用链表,但是使用C++标准库中的list还是会超时,其原因在于模拟的过程中存在多次的查找和删除,因此应该空间换时间来避免查找和删除。

为了避免在查找时进行遍历,可以记录每个盒子的相对位置,也就是使用数组模拟双向链表,这样在进行查找和删除时就可以直接定位到对应的箱子,后面只需要更新盒子之间的链接关系即可。

稍微复杂一些的操作是交换,因为双向链表的交换操作需要处理元素相邻的特殊情况。代码中使用的方式是先删除X,然后将X插入到Y的右面,此时如果初始时X不在Y的左面,则在将Y删除并移动到X的位置——这种方式只适用于XY不相邻或者XY的左面的情况。

麻烦的还有输出,由于链表长度可奇可偶,且只输出奇数位置盒子编号的和,为了简化判断是否到达链表尾部的逻辑,代码中使用count变量提前计算出了需要求和元素的数量。

#include <iostream>
#include <vector>

using namespace std;

struct Box
{
	int no;
	int left, right;
	Box(int n, int l, int r) : no(n), left(l), right(r) {};
};

struct Boxes
{
	vector<Box> boxes;
	Boxes(int n)
	{
		boxes.emplace_back(0, 0, 1);
		for (int i = 1; i <= n; i++)
		{
			boxes.emplace_back(i, i - 1, i + 1);
		}
		boxes.emplace_back(0, n, 0);
	}
	void remove(int X)
	{
		Box &x = boxes[X];
		boxes[x.left].right = x.right;
		boxes[x.right].left = x.left;
	}
	void InsertLeft(int X, int Y)
	{
		Box &y = boxes[Y];
		boxes[X].right = Y;
		boxes[X].left = y.left;
		boxes[y.left].right = X;
		y.left = X;
	}
	void InsertRight(int X, int Y)
	{
		Box &y = boxes[Y];
		boxes[X].left = Y;
		boxes[X].right = y.right;
		boxes[y.right].left = X;
		y.right = X;
	}
	void swap(int X, int Y)
	{
		if (boxes[Y].right == X) return swap(Y, X);
		int XRight = boxes[X].right;
		remove(X);
		InsertRight(X, Y);
		if (XRight != Y) {
			remove(Y);
			InsertLeft(Y, XRight);
		}
	}
	unsigned int GetOddSum(bool reverse)
	{
		unsigned int sum = 0;
		size_t count = (boxes.size() - 2) / 2 + boxes.size() % 2;
		if (reverse) {
			int no = boxes.back().left;
			for (size_t i = 0; i < count; i++)
			{
				sum += no;
				no = boxes[no].left;
				no = boxes[no].left;
			}
		}
		else {
			int no = boxes.front().right;
			for (size_t i = 0; i < count; i++)
			{
				sum += no;
				no = boxes[no].right;
				no = boxes[no].right;
			}
		}
		return sum;
	}
};

int main()
{
	int cases = 1;
	int n, m;
	while (cin >> n) {
		Boxes boxes(n);
		cin >> m;
		bool reverse = false;
		int cmd, X, Y;
		for (int i = 0; i < m; i++)
		{
			cin >> cmd;
			switch (cmd)
			{
			case 1:
				cin >> X >> Y;
				boxes.remove(X);
				if (reverse) boxes.InsertRight(X, Y);
				else boxes.InsertLeft(X, Y);
				break;
			case 2:
				cin >> X >> Y;
				boxes.remove(X);
				if (reverse) boxes.InsertLeft(X, Y);
				else boxes.InsertRight(X, Y);
				break;
			case 3:
				cin >> X >> Y;
				boxes.swap(X, Y);
				break;
			case 4:
				reverse = reverse == false;
				break;
			}
		}
		cout << "Case " << cases++ << ": " << boxes.GetOddSum(reverse) << endl;
	}
	return 0;
}
/*
6 4
1 1 4
2 3 5
3 1 6
4
6 3
1 1 4
2 3 5
3 1 6
100000 1
4
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值