【题目】
盒子编号1,2,3...n。
可执行四种指令:1XY 表示把盒子X移动到盒子Y左边(若X已经在Y的左边则忽略指令)
2XY表示把盒子X移动到盒子Y右边(若X已经在Y的右边则忽略指令)
3XY表示交换盒子X和Y的位置
4表示反转整条链
指令保证合法,X不等于Y。
输入:
每组数据第一行为盒子个数n和指令条数m,以下m行每行包括一条指令,每组数据输出一行,即所有奇数未知的盒子编号之和,位置从左到右编号为1~n。
样例输入:
6 4
1 1 4
2 3 5
3 1 6
4
6 3
1 1 4
2 3 5
3 1 6
10000 1
4
样例输出:
Case 1: 12
Case 2:9
Case 3:2500050000
拿链表写的,很简单的链表操作。篇幅很大。
#include<iostream>
using namespace std;
int bigflag = 0;
struct node {
int x;
node * next;
};
int main() {
int n, m;
while (cin >> n >> m)
{
node *head = new node;;
head->x = 1;
node *temp = head;
for (int i = 2; i <= n; i++) {
node *p = new node;
p->x = i;
temp->next = p;
temp = p;
}
temp->next = NULL;
int a, b, c;
for (int flag = 0; flag < m; flag++) {
cin >> a;
if (a == 1) {
cin >> b >> c;
int i, j;
node *zz = head;
node *hh = head;
if (zz->x == b)i = 0;
else {
for (i = 1; i < n; i++) {
if (zz->next->x == b)break;
zz == zz->next;
}
}//真实位置i+1,要单独考虑在头的情况;
if (hh->x == c)j = 0;
else {
for (j = 1; j < n; j++) {
if (hh->next->x == c)break;
hh = hh->next;
}
}//真实情况j+1;
if (i + 1 + 1 != j + 1) {
if (i == 0) {
head = zz->next;
zz->next = hh->next;
hh->next = zz;
}
else {
node *s = new node;
s = zz->next;
zz->next = zz->next->next;
s->next = hh->next;
hh->next = s;
}
}
node * w = head;
}
else if (a == 2) {
cin >> b >> c;
int i, j;
node *zz = head;
node *hh = head;
if (zz->x == b)i = 0;
else {
for (i = 1; i < n; i++) {
if (zz->next->x == b)break;
zz == zz->next;
}
}//真实位置i+1,要单独考虑在头的情况;
for (j = 1; j < n; j++) {
if (hh->x == c)break;
hh = hh->next;
}
if (i + 1 != j + 1) {
if (i == 0) {
head = zz->next;
zz->next = hh->next;
hh->next = zz;
}
else {
node *p = new node;
p = zz->next;
zz->next = zz->next->next;
p->next = hh->next;
hh->next = p;
}
}
}
else if (a == 3) {
cin >> b >> c;
node *zz = head;
node *hh = head;
int i, j;
for (i = 1; i < n; i++) {
if (zz->x == b)break;
zz = zz->next;
}
for (j = 1; j < n; j++) {
if (hh->x == c)break;
hh = hh->next;
}
int temp;
temp = zz->x;
zz->x = hh->x;
hh->x = temp;
node * e = head;
int sum = 0;
}
else if (a == 4) {
node *mid = head;
node *tail = head->next;
for (int i = 1; i < n; i++) {
node *pre = tail->next;
tail->next = mid;
mid = tail;
tail = pre;
}
node *mm = mid;
head = mid;
}
}
node *mark = head;
int sum = 0;
for (int i = 1; i <= n; i = i + 2) {
sum = sum + mark->x;
mark = mark->next->next;
}
cout << "Case "<<bigflag+1<<": "<<sum << endl;
bigflag++;
}
system("pause");
return 0;
}
【标准代码】(书上给的答案,有些地方我嫌麻烦稍微修改了下)
#include<iostream>
using namespace std;
int leftt[100001], rightt[1000001];
void link(int l, int r) {
rightt[l] = r;
leftt[r] = l;
}
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
leftt[i] = i - 1;
rightt[i] = (i + 1) % (n + 1);
}
leftt[0] = n;
rightt[0] = 1;//非常简单的双向链表,重点在于箱子编号有序,这样很好写,箱子编号如果无序,这个链表也难写出来。
//标注头尾
int op, x, y;
bool mark = false;
for (int ii = 0; ii < m; ii++) {
cin >> op;
if (op != 4) {
cin >> x >> y;
int ly = leftt[y], lx = leftt[x], ry = rightt[y], rx = rightt[x];
if (op == 1 && (leftt[y] != x)) {
link(lx, rx);
link(ly, x);
link(x, y);
}
else if (op == 2 && (rightt[y] != x)) {
link(lx, rx);
link(y, x);
link(x, ry);
}
else if (op == 3) {
int tempry = ry;
int temply = ly;
link(lx, y);
link(y, rx);
link(temply, x);
link(x, tempry);
}//这个地方需要留心,前两种情况都是只动一个,现在是动两个。
else;
}
else {
mark = !mark;
}
}
//现在就是找出奇数位置了
int flag = 0;
long long int total = 0;
for (int i = 1; i <= n; i++) {
flag = rightt[flag];
if (i % 2 == 1)total = total + flag;
}
if (mark && (n % 2 == 0))total = (long long)n*(n + 1) / 2 - total;
cout << "Case " << 1 << " : " << total << endl;
system("pause");
return 0;
}
篇幅明显缩短。
1首先是箱子编号有顺序,这样用两个数组代替双向链表非常巧妙!!!(这也是这道题其实很水但是一定要po出来的原因)
2反转在单链表里写起来也很麻烦,在这里可以看出他找到了两条规律,(1)反转命令在前三条命令前或者三条命令后都没有关系(所以我们把它统一放在最后处理)
(2)不要求全部输出反转链表,只是求奇数位置之和。用单向链表必须完全反转(不然无法得到奇数位置的盒子编号)双向链表也比较麻烦,数组自带左右位置,不需要反转,找到值的规律,n为奇数反转得到的和还是这些奇数的和,n为偶数反转结果就是总和(1到n)减去结果。(大大节省时间,非常秒!)
(3)数组第0个位置用来存储首尾!!!这样在定位奇数位置的时候非常方便。
非常简洁强大的双链表数组实现(自带左右,标记头尾,注意赋值的时候也挺重要。)
不一定要实现每一条命令,根据题目去寻找规律会简化很多。
其实已经算一个双向循环链表了。
用到【双向or循环链表】并且不删除元素的时候考虑。