有点长,放个目录。
题意
这题被强行魔改了不知道几次。
给一个填了数字的矩阵,把一个矩阵分成两块,每一块联通并且只能是“阶梯状”的,按原文说是同一块一个点走到另一个点最多只拐一个弯。
求最小化两块内部的极差的最大值。
思路
做这道题虽然花了很长时间,但是很有收获啊。学了标程算法,自己也想出一种。
贪心
第一眼看这一题明显是一个贪心啊qwq。先假装所有数都在同一个块中,也钦定矩阵的最小值必须放在此块内,那么为了减小这块的极差,我们就必须取出最大的放到另一块中(不然就一点用都没有啊)。在取出一个数时,为了维持两块的形状,把他右下方矩形整个都取出,放入另一个块。
然后为了防止最小值并到其他角更优,把矩阵旋转90,180,270度再重复上述操作。
然后卡卡常就好了。 还学了一些清空queue的奇技淫巧。。。
二分答案
代码比贪心不知道短到哪里去了,常数也比贪心不知道小到哪里去了。一个可以写成 O ( n l o g n ) O(nlogn) O(nlogn)的check写成 O ( n 2 ) O(n^2) O(n2)也很稳。
于是思路还是钦定最小值在左上,然后很容易发现极差的单调性,也可以根据题目的说法“最大的最小”来猜测。于是二分极差,小于等于 m i n v a l + m i d minval+mid minval+mid且满足形状要求的数可以放在左上,其他数再求极差,如果小于等于 m i d mid mid,那么 m i d mid mid可行。
旋转一下答案get。
样例
能拍出我的错的样例:
/*
4 4
1 1 1 1
1 4 1 5
1 1 2 5
1 5 5 5
-----
ans = 3
*/
/*
3 4
10 12 1 2
14 12 12 11
6 12 1 13
---------
ans = 12
*/
/*
8 6
23 23 10 11 16 21
15 26 19 28 19 20
25 26 28 16 15 11
11 8 19 11 15 24
14 19 15 14 24 11
10 8 11 7 6 14
23 5 19 23 17 17
18 11 21 14 20 16
----------
ans = 18
*/
/*
10 9
625 978 347 845 971 929 924 953 589
741 289 975 767 384 795 633 159 842
261 1002 980 169 655 342 139 209 658
269 670 110 697 545 593 555 400 709
901 122 794 0 477 86 833 570 789
47 708 129 967 513 915 954 351 675
969 437 212 466 76 346 717 76 112
696 941 746 916 318 540 20 886 868
255 228 897 470 69 855 940 338 923
672 211 662 703 752 850 477 383 128
------------
ans = 967
*/
/*
6 5
43 25 2 93 102
44 67 71 59 62
93 47 39 6 59
11 71 96 28 65
56 84 84 46 106
2 103 58 101 24
--------
ans = 91
solution:
43 25 2 93 | 102
44 67 71 59| 62
93 47 39 6 | 59
------
11 71 | 96 28 65
----
56 | 84 84 46 106
2 |103 58 101 24
*/
/*
20 15
4 39 27 22 10 43 0 27 7 49 7 39 40 26 40
25 25 2 7 24 26 10 14 6 46 35 30 32 0 37
37 2 48 46 13 45 4 38 48 31 6 28 27 14 6
38 51 10 25 33 43 28 37 43 21 0 38 49 48 34
28 32 42 16 32 22 51 25 33 33 0 0 35 46 11
8 40 28 39 43 48 27 38 18 23 4 0 50 21 20
5 38 29 11 34 22 33 36 43 12 8 22 12 51 2
30 23 28 40 0 6 43 26 51 39 29 13 27 34 19
23 51 13 26 20 51 25 35 17 30 51 1 42 36 36
49 14 8 45 41 12 38 21 1 16 12 32 8 42 50
6 37 5 15 44 5 1 33 48 23 31 8 50 29 3
10 32 44 48 12 13 40 0 14 44 5 4 52 14 24
21 25 34 16 7 8 20 3 29 38 3 31 47 17 22
0 28 33 8 30 18 6 42 44 30 47 0 22 29 49
10 38 21 8 22 26 49 2 29 45 23 27 20 8 45
20 27 49 21 46 34 28 37 41 37 44 33 20 44 48
2 7 21 39 5 8 32 25 46 49 0 32 1 50 10
12 25 42 30 16 45 8 22 25 49 0 38 25 40 47
17 15 40 42 37 5 35 38 0 20 5 33 52 48 27
39 16 40 45 35 9 44 34 7 33 16 29 24 2 24
---------------
ans = 51
*/
/*
3 10
1 13 1 65 17 85 61 71 81 61
97 9 37 91 1 76 36 1 21 41
31 26 69 41 24 81 13 51 21 85
-------------
ans = 88
*/
代码
在放正解之前当然要看一看虽然已经被hack掉但是A了65分的代码(还有一些样例):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int dx[4] = {-1, 0, 1, 0};
const int dy[4] = {0, -1, 0, 1};
const int inf = 1e9;
const int N = 2010;
int n, m, a[N][N], ans;
struct NODE{
int x, y, val;
bool operator < (const NODE &u)const{
return val < u.val;
}
bool operator > (const NODE &u)const{
return val > u.val;
}
};
priority_queue<NODE> gg;
priority_queue<NODE, vector<NODE>, greater<NODE> > lg;
int minval, maxval, minl, maxl, minr, maxr;
queue<pair<int, int> > q;
int used[N][N], b[N][N];
inline void Rotate()
{
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j)
b[j][n-i+1] = a[i][j];
swap(n, m);
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j)
a[i][j] = b[i][j];
}
inline void Update(int sx, int sy, int dir, int &mina, int &maxa)
{
// cout << sx << " " << sy << " " << dir << ":::---_____---:::" << endl;
while (!q.empty()) q.pop();
q.push(make_pair(sx, sy)); used[sx][sy] = dir;
while (!q.empty()){
int ux = q.front().first;
int uy = q.front().second;
// cout << ux << " " << uy << endl;
q.pop();
if (mina == -1) mina = a[ux][uy];
mina = min(mina, a[ux][uy]);
maxa = max(maxa, a[ux][uy]);
for (int k = dir; k <= dir+1; ++ k){
int nx = ux+dx[k];
int ny = uy+dy[k];
if (used[nx][ny] != -1 || nx <= 0 || nx > n || ny <= 0 || ny > m) continue;
used[nx][ny] = dir;
q.push(make_pair(nx, ny));
}
}
}
inline void Solve()
{
minl = maxl = minr = maxr = -1;
memset(used, -1, sizeof(used));
while (!gg.empty()) gg.pop();
while (!lg.empty()) lg.pop();
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j){
gg.push((NODE){i, j, a[i][j]});
lg.push((NODE){i, j, a[i][j]});
}
while (1){
maxval = -1;
while (!gg.empty()){
int x = gg.top().x;
int y = gg.top().y;
if (used[x][y] != -1) gg.pop();
else{
maxval = gg.top().val;
break;
}
}
if (maxval == -1) break;
while (!lg.empty()){
int x = lg.top().x;
int y = lg.top().y;
if (used[x][y] != -1) lg.pop();
else{
minval = lg.top().val;
break;
}
}
// cout << maxval << " " << minval << " !" << endl;
int pminl = minl, pmaxl = maxl, pminr = minr, pmaxr = maxr;
if (maxl-maxr >= maxr-minr){
while (gg.top().val == maxval){
int x = gg.top().x;
int y = gg.top().y;
if (used[x][y] == -1) Update(x, y, 2, minr, maxr);
gg.pop();
}
} else {
while (lg.top().val == minval){
int x = lg.top().x;
int y = lg.top().y;
if (used[x][y] == 2){
if (pminl == -1) ans = min(ans, maxval-minval);
else ans = min(ans, min(max(max(maxval, pmaxl)-pminl, pmaxr-pminr), max(pmaxl-pminl, pmaxr-min(minval, pminr))));
return;
}
else if (used[x][y] == -1) Update(x, y, 0, minl, maxl);
lg.pop();
}
}
// cout << minl << " " << maxl << " " << minr << " " << maxr << endl;
}
ans = min(ans, max(maxl-minl, maxr-minr));
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j)
scanf("%d", &a[i][j]);
ans = inf;
for (int i = 0; i < 4; ++ i){
// cout << endl;
// for (int j = 1; j <= n; ++ j){
// for (int k = 1; k <= m; ++ k)
// cout << a[j][k] << " ";
// cout << endl;
// }
Solve();
if (i < 3) Rotate();
}
printf("%d\n", ans);
return 0;
}
贪心:
#pragma GCC optimize("3", "inline", "Ofast")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int dx[2] = {1, 0};
const int dy[2] = {0, 1};
const int inf = 1e9;
const int N = 2010;
int n, m, a[N][N], ans;
struct NODE{
int x, y, val;
bool operator < (const NODE &u)const{
return val < u.val;
}
bool operator > (const NODE &u)const{
return val > u.val;
}
};
priority_queue<NODE> q;
int minval, mina, maxa;
char used[N][N];
queue<pair<int, int> > p;
inline void chkMin(int &x, int y){if (x > y) x = y;}
inline void chkMax(int &x, int y){if (x < y) x = y;}
int b[N][N];
inline void Rotate()
{
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j)
b[j][n-i+1] = a[i][j];
swap(n, m);
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j)
a[i][j] = b[i][j];
}
int ux, uy, nx, ny;
inline void Update(int sx, int sy)
{
while (!p.empty()) p.pop();
p.push(make_pair(sx, sy)); used[sx][sy] = 1;
while (!p.empty()){
ux = p.front().first;
uy = p.front().second;
p.pop();
chkMin(mina, a[ux][uy]);
chkMax(maxa, a[ux][uy]);
for (int k = 0; k < 2; ++ k){
nx = ux+dx[k];
ny = uy+dy[k];
if (used[nx][ny] || nx <= 0 || nx > n || ny <= 0 || ny > m) continue;
used[nx][ny] = 1;
p.push(make_pair(nx, ny));
}
}
}
void Solve()
{
memset(used, 0, sizeof(used));
q = priority_queue<NODE>();
// 因为while基本到一半的时候就break掉了,很多元素还留在堆里
// 如果while (!q.empty()) q.pop()就稳稳地TLE了,而且常数大了10倍,有点可怕
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j){
q.push((NODE){i, j, a[i][j]});
}
chkMin(ans, q.top().val-minval);
mina = inf; maxa = 0;
while (!q.empty()){
while (!q.empty() && used[q.top().x][q.top().y]) q.pop();
if (q.empty()) break;
Update(q.top().x, q.top().y);
while (!q.empty() && used[q.top().x][q.top().y]) q.pop();
if (q.empty()) break;
if (maxa-mina > q.top().val-minval){
chkMin(ans, maxa-mina);
break;
}
chkMin(ans, q.top().val-minval);
}
}
int main()
{
scanf("%d%d", &n, &m);
minval = inf;
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j){
scanf("%d", &a[i][j]);
if (a[i][j] < minval) minval = a[i][j];
}
ans = inf;
for (int i = 0; i < 4; ++ i){
Solve();
if (i < 3) Rotate();
}
printf("%d\n", ans);
return 0;
}
二分答案:
#include<bits/stdc++.h>
using namespace std;
const int inf = 1e9;
const int N = 2010;
int n, m, a[N][N], ans, minval, maxval, maxl[N][N], minr[N][N];
inline void chkMin(int &x, int y){if (x > y) x = y;}
inline void chkMax(int &x, int y){if (x < y) x = y;}
int b[N][N];
void Rotate()
{
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j)
b[j][n-i+1] = a[i][j];
swap(n, m);
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j)
a[i][j] = b[i][j];
}
void Initialize()
{
for (int i = 1; i <= n; ++ i){
maxl[i][0] = 0;
for (int j = 1; j <= m; ++ j)
maxl[i][j] = max(maxl[i][j-1], a[i][j]);
maxl[i][m+1] = inf;
minr[i][m+1] = inf;
for (int j = m; j >= 1; -- j)
minr[i][j] = min(minr[i][j+1], a[i][j]);
}
}
bool Check(int l)
{
int minv = inf, pre = m+1;
for (int i = 1; i <= n; ++ i){
bool fl = 1;
for (int j = 1; j <= pre; ++ j)
if (maxl[i][j] > l){
chkMin(minv, minr[i][j]);
pre = j;
fl = 0;
break;
}
if (fl) chkMin(minv, minr[i][pre]);
}
return (maxval-minv <= l-minval);
}
void Solve()
{
int l = 0, r = inf, mid, ret;
while (l <= r){
mid = (l+r)>>1;
if (Check(mid+minval))
ret = mid, r = mid-1;
else
l = mid+1;
}
chkMin(ans, ret);
}
int main()
{
scanf("%d%d", &n, &m);
maxval = 0;
minval = inf;
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j){
scanf("%d", &a[i][j]);
chkMax(maxval, a[i][j]);
chkMin(minval, a[i][j]);
}
ans = inf;
for (int i = 0; i < 4; ++ i){
Initialize();
Solve();
if (i < 3) Rotate();
}
printf("%d\n", ans);
return 0;
}