移动盒子:
一行有n 个盒子,从左到右编号为1~n。模拟以下4种命令。
1 X Y :将盒子X 移动到Y 的左侧(如果X 已经在Y 的左侧,则忽略此项)。
2 X Y :将盒子X 移动到Y 的右侧(如果X 已经在Y 的右侧,则忽略此项)。
3 X Y :交换盒子X 和Y 的位置。
4:翻转整行盒子序列。
命令保证有效,即X 不会 与 Y相等
【举例说明】
有6个盒子,执行1 1 4,即1移动到4的左侧,变成2 31 4 5 6。然后执行2 3 5,即3移动到5的右侧,变成2 1 4 5 3 6。接着执行3 1 6,即交换1和6的位置,变成2 6 4 5 3 1。最后执行4,即翻转整行序列,变成1 3 5 4 6 2。
【输入输出】
输入:
最多有10个测试用例。每个测试用例的第1行都包含两个整数n 和m (1≤n , m ≤100 000),下面的m 行,每行都包含一个命令。
输出:
对于每个测试用例,都单行输出奇数索引位置的数字总和。
思路:双向链表用inv代表链表反转与否,单个操作的指令与inv有关。设link函数连接左右盒子
#include <stdio.h>
const int maxn = 1000005; // 定义最大节点数
int left[maxn], right[maxn]; // 左右节点数组
void link(int l, int r) {//实现l与r合并
right[l] = r;
left[r] = l;
}
int main(void) {
int n, m, kase = 0;
while (scanf("%d%d", &n, &m) == 2) {
for (int i = 1; i <= n; i++) {
left[i] = i - 1;
right[i] = (i + 1) % (n + 1);//n+1时输0
}
left[0] = n;
right[0] = 1;
int op, x, y, inv = 0;
while (m--) {
scanf("%d", &op);
if (op < 3 && inv) {
op = 3 - op; // 链表在中间翻转后,操作基础为已翻转链表
}
int lx = left[x], rx = right[x], ly = left[y], ry = right[y];
if (op == 1) {
if (x == ly) continue;
link(lx, rx);
link(ly, x);
link(x, y);
} else if (op == 2) {
if (x == ry) continue;
link(lx, rx);
link(y, x);
link(x, ry);
} else if (op == 3) {
if (right[x] == y) {
link(lx, y);
link(y, x);
link(x, ry);
} else if (right[y] == x) {
link(lx, y);
link(y, x);
link(x, ry);
} else {
link(lx, y);
link(ly, x);
link(x, ry);
}
}
}
int b = 0;
long long ans = 0;
for (int i = 1; i <= n; i++) {
b = right[b];
if (i % 2 == 1) ans += b;
}
if (inv && n % 2 == 0) ans = (long long)n * (n + 1) / 2 - ans;
printf("Case %d: %lld\n", ++kase, ans);
}
return 0;
}
我的初写代码:
#include<stdio.h>
int main(void){
int n,m;
scanf("%d%d",&n,&m);
int left[n+1];
int right[n+1];
for(int i=1;i<n+1;i++){
left[i]=(i==1)?0:i-1;
right[i]=(i==n)?0:i+1;
}
int no_,x,y;
int re=0;
for(int i=0;i<m;i++){
scanf("%d",&no_);
if(no_==1&&left[y]!=x){
scanf("%d%d",&x,&y);
left[y]=x;
right[x]=y;
}
else if(no_==2&&right[y]!=x){
scanf("%d%d",&x,&y);
right[y]=x;
left[x]=y;
}
else if(no_==3){
scanf("%d%d",&x,&y);
int u=y;
int ok=1;
while(left[u]!=x){//左查
if(left[u]==0){
ok=0;
break;
}
u=left[u];
}
if(ok){
int v=left[left[u]],w=left[y];
right[v]=y;
right[w]=x;
left[u]=y;
}
ok=1;
u=y;
while(right[u]!=x){//右查
if(right[u]==0){
ok=0;
break;
}
u=right[u];
}
if(ok){
int v=right[right[u]],w=right[y];
right[v]=y;
right[w]=x;
right[u]=y;
}
}
if(no_==4){
re=!re;
}
}
int sum=0;
int begin=(re)?left[0]:right[0];
while(begin){
begin=(re)?left[left[begin]]:right[right[begin]];
sum+=begin;
}
printf("%d",sum);
}
结果不输出
原因:left和right数组没绑定,易出现更新遗漏。反转链表放最后反转
收获:反转链表输出可输出原数据相加(别忘了偶数情况)