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