其他分享
首页 > 其他分享> > G 九峰与蛇形填数(链表+倍增)

G 九峰与蛇形填数(链表+倍增)

作者:互联网

传送门


题目大意

给出新“蛇形填数”的规则,一个 n ∗ n ( n ≤ 2000 ) n*n(n \leq 2000) n∗n(n≤2000)的初始全为0的矩阵,有最多 m m m次操作,每次操作可以让一个子矩阵进行一次蛇形填数,求出最终矩阵每个位置的数。

解题思路

先放上我的解法(似乎偏小众一点)

对于每个位置,最终优先保存的是最晚进行的修改,这启发我们将询问倒着操作,每次操作时如果已经被填好的位置直接跳过,理想下每个位置只被操作一次,但是现实很残酷,如何跳过一个数?根据链表的思想,我们以行为主体,记录每行每个位置之前已经填好的最左边和最右边没填的位置(因为填数时是连续的因此不会中间有没填的数),然后仍需要枚举行,但在行上进行跳跃。

但最坏的情况是,从所有的询问是从左到右依次操作的,即使每次按链表那样去找,依旧会导致最坏的情况 O ( n 2 ∗ m ) O(n^2*m) O(n2∗m),然后我们必须想办法不让这个最坏的情况发生!对于下图的链表,我们可以在查询最右端节点时,顺便依次将当前节点和下下一个节点相连。显然每次链表的边数就会减少一半,然后我们就可以在均摊 O ( n ∗ m ∗ l o g n ) O(n*m*logn) O(n∗m∗logn)的时间复杂度下进行修改,总体的时间复杂度为 O ( n ∗ n + n ∗ m ∗ l o g n ) O(n*n+n*m*logn) O(n∗n+n∗m∗logn)。

350 m s 350ms 350ms通过此题,跑的不算慢,但恶心的是细节过多,拿到思路半小时敲代码两小时…

在这里插入图片描述

这题做法好像还很多,并查集和线段树我都没想到怎么实现,明天抽空扩展几种其他思路

#include <bits/stdc++.h>

using namespace std;
#define ENDL "\n"
typedef long long ll;
const int up = 1e9;
const int Mod = 1e9 + 7;
const int maxn = 1e4 + 10;

struct node {
    int x, y, k;
} p[maxn];

int n, m;
int a[2020][2020];
int L[2020][2020], R[2020][2020];

void printL() {
    for (int i = 1; i <= n; i++) {
        cout << L[i][1];
        for (int j = 2; j <= n; j++) {
            cout << " " << L[i][j];
        }
        cout << ENDL;
    }
}

void printR() {
    for (int i = 1; i <= n; i++) {
        cout << R[i][1];
        for (int j = 2; j <= n; j++) {
            cout << " " << R[i][j];
        }
        cout << ENDL;
    }
}

void solve(int x, int y, int k) {
    int cur = 1;
    for (int i = x, op = 1; i <= x + k - 1; i++, op = 1 - op) {
        if (op) {
            for (int j = y; j <= y + k - 1;) {
                if (!R[i][j]) {
                    a[i][j] = cur++;
                    R[i][j] = max(R[i][j], y + k - j);
                    L[i][j] = max(L[i][j], j - y + 1);
                    j++;
                } else {
                    int st = j + R[i][j], t = j;
                    while (R[i][st]) R[i][t] += R[i][st], t = st, st += R[i][st];
                    cur += min(st - j, y + k - 1 - j); //防止越过子矩阵的右边界
                    j = st;
                    if (j > y + k - 1) cur++;  //越过了本次操作子矩阵的右边界
                }
            }
        } else {
            for (int j = y + k - 1; j >= y;) {
                if (!L[i][j]) {
                    a[i][j] = cur++;
                    R[i][j] = max(R[i][j], y + k - j);
                    L[i][j] = max(L[i][j], j - y + 1);
                    j--;
                } else {
                    int st = j - L[i][j], t = j;
                    while (L[i][st]) L[i][t] += L[i][st], t = st, st -= L[i][st];
                    cur += min(j - st, j - y); //防止越过子矩阵的左边界
                    j = st;
                    if (j < y) cur++;  //越过了本次操作子矩阵的左边界
                }
            }
        }
    }
}

void print() {
    for (int i = 1; i <= n; i++) {
        cout << a[i][1];
        for (int j = 2; j <= n; j++) {
            cout << " " << a[i][j];
        }
        cout << ENDL;
    }
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        cin >> p[i].x >> p[i].y >> p[i].k;
    }
    for (int i = m; i >= 1; i--) {
        solve(p[i].x, p[i].y, p[i].k);
    }
    print();
    return 0;
}

标签:cout,int,矩阵,cur,st,链表,填数,2020,九峰
来源: https://blog.csdn.net/qq_44691917/article/details/113872031