Snowy Smile
Time Limit: 4000/4000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 525 Accepted Submission(s): 165
Problem Description
There are n pirate chests buried in Byteland, labeled by 1,2,…,n. The i-th chest’s location is (xi,yi), and its value is wi, wi can be negative since the pirate can add some poisonous gases into the chest. When you open the i-th pirate chest, you will get wi value.
You want to make money from these pirate chests. You can select a rectangle, the sides of which are all paralleled to the axes, and then all the chests inside it or on its border will be opened. Note that you must open all the chests within that range regardless of their values are positive or negative. But you can choose a rectangle with nothing in it to get a zero sum.
Please write a program to find the best rectangle with maximum total value.
Input
The first line of the input contains an integer T(1≤T≤100), denoting the number of test cases.
In each test case, there is one integer n(1≤n≤2000) in the first line, denoting the number of pirate chests.
For the next n lines, each line contains three integers xi,yi,wi(−109≤xi,yi,wi≤109), denoting each pirate chest.
It is guaranteed that ∑n≤10000.
Output
For each test case, print a single line containing an integer, denoting the maximum total value.
Sample Input
2
4
1 1 50
2 1 50
1 2 50
2 2 -500
2
-1 1 5
-1 1 1
Sample Output
100
6
題目大概意思:
給出平面上 個點,第 個點的座標是 ,權值爲 , ,用一個任意大小的邊與座標軸平行的矩形覆蓋點,求矩形能覆蓋的點的權值之和的最大值。
分析:
首先對座標進行離散化處理,則問題轉化爲了求一個 的矩陣 的所有子矩陣的權值之和的最大值。而由於只有 個點,故 中至多有 個非零元。
對於一般的矩陣求最大子矩陣和,時間複雜度是 或 :
對於一維的最大子段和,我們可以使用尺取法在 的時間複雜度下計算出來,也可以通過使用線段樹維護區間最大子段和,在 的時間複雜度下計算出來。
而對於二維的問題,我們可以枚舉出矩陣的上下邊界,再計算每一列處於上下邊界之間的元素的和,將其轉化爲 個一維問題來解決。
但 的時間複雜度無法在時間限制內解決問題。因此我們考慮如何利用 是稀疏矩陣這一特性降低時間複雜度。
考慮到矩陣中至多有 個非零元,因此每一行只有 個均攤的非零元,因此如果我們已經計算出了以 爲上下邊界的子矩陣 的每一列的元素之和所組成的向量 ,那麼對於以 爲上下邊界的子矩陣 的每一列的元素之和所組成的向量 , 與 之間在均攤下只有 個元素是不同的。因此如果我們使用了線段樹維護 的最大子段和,則可以通過修改 個元素將其轉爲維護 的最大子段和的線段樹,而線段樹的每次更新的時間複雜度爲 ,因此我們藉助維護前一個上下邊界內元素的最大子段和的線段樹,可以在 的時間複雜度內轉變爲維護當前上下邊界內元素的最大子段和的線段樹。那麼共有 種邊界,在每兩種曼哈頓距離爲 的邊界之間轉移的時間複雜度是 ,故算法的總時間複雜度是 .
下面貼代碼:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAX_N = 2005;
struct Star
{
int x;
int y;
int w;
};
Star p[MAX_N];
int kx[MAX_N];
int ky[MAX_N];
ll A[MAX_N][MAX_N];
int G[MAX_N][MAX_N];
int deg[MAX_N];
struct P
{
ll left;
ll right;
ll all;
ll rms;
P() :left(0), right(0), all(0), rms(0) {}
P(const ll& left, const ll& right, const ll& all, const ll& rms)
:left(left), right(right), all(all), rms(rms) {}
};
P dat[(1 << 13) + 5];
int _n;
ll _max(const ll& a, const ll& b);
P unite(P ls, P rs);
void _init(const int n);
void update(const ll& x, int p);
P query(const int& left, const int& right, const int k, const int begin, const int end);
int main()
{
int T, n;
scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
int kxcnt = 0;
int kycnt = 0;
for (int i = 0; i <= n; i++)
{
memset(A[i], 0, (n + 1) * sizeof(A[0][0]));
}
memset(deg, 0, sizeof(deg));
for (int i = 0; i < n; i++)
{
Star& cur = p[i];
scanf("%d%d%d", &cur.x, &cur.y, &cur.w);
kx[kxcnt++] = cur.x;
ky[kycnt++] = cur.y;
}
sort(kx, kx + kxcnt);
sort(ky, ky + kycnt);
kxcnt = unique(kx, kx + kxcnt) - kx;
kycnt = unique(ky, ky + kycnt) - ky;
for (int i = 0; i < n; i++)
{
Star& cur = p[i];
cur.x = lower_bound(kx, kx + kxcnt, cur.x) - kx;
cur.y = lower_bound(ky, ky + kycnt, cur.y) - ky;
A[cur.x][cur.y] += cur.w;
}
for (int i = 0; i < kxcnt; i++)
{
for (int j = 0; j < kycnt; j++)
{
if (A[i][j])
{
G[i][deg[i]++] = j;
}
}
}
ll ans = 0;
for (int i = 0; i < kxcnt; i++)
{
_init(kycnt);
for (int j = i; j < kxcnt; j++)
{
for (int k = 0; k < deg[j]; k++)
{
update(A[j][G[j][k]], G[j][k]);
}
ans = _max(query(0, n - 1, 0, 0, _n).rms, ans);
}
}
printf("%lld\n", ans);
}
return 0;
}
ll _max(const ll& a, const ll& b)
{
return a > b ? a : b;
}
P unite(P ls, P rs)
{
P res;
res.all = ls.all + rs.all;
res.left = _max(ls.left, ls.all + rs.left);
res.right = _max(rs.right, rs.all + ls.right);
res.rms = _max(_max(ls.rms, rs.rms), ls.right + rs.left);
return res;
}
void _init(const int n)
{
_n = 1;
while (_n < n)
{
_n <<= 1;
}
--_n;
memset(dat, 0, 2 * (_n + 1) * sizeof(dat[0]));
}
void update(const ll& x, int p)
{
p += _n;
P& cur = dat[p];
cur.all += x;
cur.left = cur.right = cur.rms = _max(0ll, cur.all);
while (p)
{
p = (p - 1) >> 1;
dat[p] = unite(dat[(p << 1) + 1], dat[(p << 1) + 2]);
}
}
P query(const int& left, const int& right, const int k, const int begin, const int end)
{
if (left <= begin && end <= right)
{
return dat[k];
}
else if (left <= end && begin <= right)
{
return unite
(
query(left, right, (k << 1) + 1, begin, (begin + end) >> 1),
query(left, right, (k << 1) + 2, ((begin + end) >> 1) + 1, end)
);
}
return P();
}