UVA - 12657 Boxes in a Line 【数组模拟双向链表】


UVA 12657 Boxes in a Line

You have n boxes in a line on the table numbered 1 . . . n from left to right. Your task is to simulate 4
kinds of commands:
• 1 X Y : move box X to the left to Y (ignore this if X is already the left of Y )
• 2 X Y : move box X to the right to Y (ignore this if X is already the right of Y )
• 3 X Y : swap box X and Y
• 4: reverse the whole line.
Commands are guaranteed to be valid, i.e. X will be not equal to Y .
For example, if n = 6, after executing 1 1 4, the line becomes 2 3 1 4 5 6. Then after executing
2 3 5, the line becomes 2 1 4 5 3 6. Then after executing 3 1 6, the line becomes 2 6 4 5 3 1.
Then after executing 4, then line becomes 1 3 5 4 6 2
Input
There will be at most 10 test cases. Each test case begins with a line containing 2 integers n, m
(1 ≤ n, m ≤ 100, 000). Each of the following m lines contain a command.
Output
For each test case, print the sum of numbers at odd-indexed positions. Positions are numbered 1 to n
from left to right.
Sample Input
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
Sample Output
Case 1: 12
Case 2: 9
Case 3: 2500050000


题意:

对链表进行4项操作,分别是 x 移到 y的左边或右边,交换 x, y 和 排列顺序逆转;

思路: 用双向链表对数据进行插入和删除,实现链表的方法有 3 种,一种是list 一种是指针链表,但事实上,这两者都会超时,  因为题目中,需要进行的操作次数有 10^5 而且,n的大小也可能为10^5,所以,很容易超时,数组模拟链表的好处就是能直接通过数字来找出位置,但是,这种情况只有当数据是连续的,比如 1到n这样的数据,才能直接找到位置;数组模拟的过程如果对指针链表的插入删除不熟悉的话,可能会比较难懂,建议先学会指针链表在去学数组模拟链表;除了第 4 个操作,其他操作都是插入删除的操作, 至于 4 这个操作,只需要一个变量去记录一下,如果是倒序的,那么接下来如果还有操作,如果 是1操作,那么 x 移到 y的左边其实就是移到右边,2的操作也是一样的道理,所以只要记录 倒序了几次,判断这个时候的链表是正序的还是倒序的就行;下面给出代码:


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;

#define Maxn 100100

int n,m,left[Maxn],right[Maxn];
long long cnt;


// 连接两个节点,只需把左边的节点的 right 指向 右边的数,同样的
// 右边的 left 指向 左边的数,如果对指针链表熟悉的话就比较好理解;
void link(int L, int R) {
    right[L] = R; left[R] = L;
}

void init() {
    right[0] = 1; left[0] = n;  // 注意别忘了给 0 这个部分初始化
    for(int i = 1 ; i <= n ; ++i) {
        left[i] = i - 1;
        right[i] = (i + 1)%(n + 1);
        //对 n+1 取余 是为了让最后一位的right[n] = 0;
    }
}

void cmd_1_2(int cd,int x,int y,int sg) {
    // 为了保险,把一个数的左右的值先保存下来
    int LX,RX,LY,RY;
    LX = left[x]; RX = right[x];
    LY = left[y]; RY = right[y];
    // 如果 sg == -1 说明链表是倒序的,那么如果cd == 1,那么执行 2的操作
    // 反过来一样,如果是 2 执行 1的操作
    if(sg == -1) cd = 3 - cd;
    //如果 y,x已经符合操作了就直接返回;
    if(left[y] == x && cd == 1) return;
    if(right[y] == x && cd == 2) return;
    // 把 x 删除,只要把 x 左边的数和右边的数连在一样就行了
    link(LX,RX);
    if(cd == 1) {
           link(LY,x);
           link(x,y);
    } else if(cd == 2) {
            link(y,x);
            link(x,RY);
    }
}

void Swap(int x,int y) {
   if(right[x] == y) swap(x,y);  // 相邻的数交换,链表会出错,这里统一当  YX这种情况处理
   int LX,RX,LY,RY;
   LX = left[x]; RX = right[x];
   LY = left[y]; RY = right[y];
   if(right[y] == x) { link(LY,x); link(x,y); link(y,RX); } // 统一的情况,如果没有上面那个swap,
   else {                                                   // 就要写两两种情况;
       link(LX,y); link(y,RX);
       link(LY,x); link(x,RY);
   }
}

void solve(int sg) {
    int p = right[0]; // 相当于头指针head
    cnt = 0;
    for(int i = 1 ; i <= n ; ++i) {
        if(i % 2 != 0) cnt+=p;
        p = right[p];
    }
    if(sg == -1 && n % 2 == 0) cnt = (long long)n*(n+1)/2 - cnt;
}

int main(void)
{
    int cmd,x,y,sigh,t = 0;
    while(scanf("%d%d",&n,&m)!=EOF) {
            memset(left,0,sizeof(left));
            memset(right,0,sizeof(right));
            sigh = 1;
            init();
        while(m--) {
            scanf("%d",&cmd);
            if(cmd == 4) sigh*=(-1);
            else {
                scanf("%d%d",&x,&y);
                if(cmd == 1 || cmd == 2) cmd_1_2(cmd,x,y,sigh);
                else if(cmd == 3) Swap(x,y);
            }
        }
        solve(sigh);
        printf("Case %d: %lld\n",++t,cnt);
    }
    return 0;
}








猜你喜欢

转载自blog.csdn.net/godleaf/article/details/80952563
今日推荐