题目:
有N个盒子排成一列,从左至右编号1~n,现有四种操作,分别如下:
1 X Y 将编号为X的盒子置于编号为Y的盒子的左边(相邻)
2 X Y 将编号为X的盒子置于编号为Y的盒子的右边(相邻)
3 X Y 将编号为X的盒子和编号为Y的盒子交换位置
4 反转整个排列
在M次操作之后,求坐标为奇数的盒子,其编号之和
例如,如果n = 6,则在执行1 1 4之后,该行变为2 3 1 4 5 6。 然后在执行2 3 5之后,该行变为2 1 4 5 3 6。 然后在执行3 1 6之后,该行变为2 6 4 5 3 1。 然后在执行4之后,行变为1 3 5 4 6 2。此时所求为1 + 5 + 6 = 12
/(ㄒoㄒ)/~~
考察链表,简单的使用数组是不明智的,第一次写用结构体完成了纯 · 双向链表,代码老长老长,还好AC了,文章最后放代码。
然后呢,就想用 list 模拟一下链表,但事情并没有那么简单:
本题如果不加思考直接写,肯定会超时,因为在查找,插入,反转上会浪费很多时间但是最坑的是这三个中任何一个不优化,都会超时
第一步优化:我们放弃查找,转而动态记录每个盒子的位置
第二步优化:如上操作,交换只需要交换值和迭代器,因为交换意味着交换盒子以及盒子的位置,少一个都不行(list.reverse也不行,我还是天真了)
第三步优化:插入时,寻址插入就好,记录迭代器就给我们提供了方便
小心溢出
实现:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <list>
using namespace std;
int main() {
int n, k, op, a, b, cp = 1;
for(; cin >> n >> k; cp++) {
list<int> L(n);
list<int>::iterator it;
vector<list<int>::iterator> IT(n+1); //用来记录盒子的位置 IT[0]是用不到的
int i = 1;
for (it = L.begin(); it != L.end(); it++, i++) {
*it = i; //这个赋值方法挺好的
IT[i] = it;
}
int fp = 0; //用来标记反转与否
while(k--) { scanf("%d", &op);
if(op == 4) {
if(fp) fp = 0; else fp = 1;
continue;
}
scanf("%d%d", &a, &b);
if(op == 3) swap(*IT[a], *IT[b]), swap(IT[a], IT[b]); //缺一不可哦
else {
L.erase(IT[a]); //L.remove(a) 也是可以的
if(!fp && op == 1 || fp && op == 2) //注意条件
IT[a] = L.insert(IT[b], a);
else {it = IT[b]; IT[a] = L.insert(++it, a);}
} //如果插在右侧 迭代器要下走一位
}
long long ans = 0; //overflow...
if(fp) fp = 0; else fp = 1;
for(it = L.begin(); it != L.end(); fp++, it++)
if(fp % 2) ans += *it;
printf("Case %d: %lld\n", cp, ans);
}
return 0;
}