HDU6638 Snowy Smile - 稀疏矩阵的最大子矩阵和 - 坐标离散化 - 线段树 - 最大子段和

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

 
 

题目大概意思:

给出平面上 n ( 1 n 2 × 1 0 3 ) n(1≤n≤2×10^3) 个点,第 i i 个点的坐标是 ( x i , y i ) (x_i,y_i) ,权值为 w i w_i 1 0 9 x i , y i , w i 1 0 9 -10^9≤x_i,y_i,w_i≤10^9 ,用一个任意大小的边与坐标轴平行的矩形覆盖点,求矩形能覆盖的点的权值之和的最大值。

 
 

分析:

首先对坐标进行离散化处理,则问题转化为了求一个 r × c   ( r , c n ) r×c\,(r,c≤n) 的矩阵 M M 的所有子矩阵的权值之和的最大值。而由于只有 n n 个点,故 M M 中至多有 n n 个非零元。

对于一般的矩阵求最大子矩阵和,时间复杂度是 O ( n 3 ) O(n^3) O ( n 3 log 2 n ) O(n^3·\log_2n)

对于一维的最大子段和,我们可以使用尺取法在 O ( n ) O(n) 的时间复杂度下计算出来,也可以通过使用线段树维护区间最大子段和,在 O ( n log 2 n ) O(n·\log_2n) 的时间复杂度下计算出来。

而对于二维的问题,我们可以枚举出矩阵的上下边界,再计算每一列处于上下边界之间的元素的和,将其转化为 n 2 n^2 个一维问题来解决。
 

O ( n 3 ) O(n^3) 的时间复杂度无法在时间限制内解决问题。因此我们考虑如何利用 M M 是稀疏矩阵这一特性降低时间复杂度。

考虑到矩阵中至多有 O ( n ) O(n) 个非零元,因此每一行只有 O ( 1 ) O(1) 个均摊的非零元,因此如果我们已经计算出了以 r 1 , r 2 r_1,r_2 为上下边界的子矩阵 M 1 M_1 的每一列的元素之和所组成的向量 v 1 \vec{v_1} ,那么对于以 r 1 , r 2 + 1 r_1,r_2+1 为上下边界的子矩阵 M 2 M_2 的每一列的元素之和所组成的向量 v 2 \vec{v_2} v 1 \vec{v_1} v 2 \vec{v_2} 之间在均摊下只有 O ( 1 ) O(1) 个元素是不同的。因此如果我们使用了线段树维护 v 1 \vec{v_1} 的最大子段和,则可以通过修改 O ( 1 ) O(1) 个元素将其转为维护 v 2 \vec{v_2} 的最大子段和的线段树,而线段树的每次更新的时间复杂度为 O ( log 2 n ) O(\log_2n) ,因此我们借助维护前一个上下边界内元素的最大子段和的线段树,可以在 O ( log 2 n ) O(\log_2n) 的时间复杂度内转变为维护当前上下边界内元素的最大子段和的线段树。那么共有 O ( n 2 ) O(n^2) 种边界,在每两种曼哈顿距离 1 1 的边界之间转移的时间复杂度是 O ( log 2 n ) O(\log_2n) ,故算法的总时间复杂度是 O ( n 2 log 2 n ) O(n^2·\log_2n) .

 
 
下面贴代码:

#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();
}

原创文章 42 获赞 22 访问量 3032

猜你喜欢

转载自blog.csdn.net/weixin_44327262/article/details/98784871