hdu6638 Snowy Smile-线段树维护最大子矩阵

题目链接

Snowy Smile

题意简述:
给出平面上n(n <= 2000)个点,点的坐标在-1e9~1e9之间,试求出最大子矩阵的和是多少。

解题思路:
考虑数据范围,2e3个点离散化后可能会有2e3个,本题中各个点是离散的,所以用普通的动态规划比较麻烦,因此本题采用 O ( N 2 l o g 2 N ) O(N^2log_2N) O(N2log2N)的线段树+枚举。
我们对x,枚举所有可能的x[i]和x[j]其中x[i]为上界,x[j]为下界,这个枚举花费时间为 O ( N 2 ) O(N^2) O(N2),而对于每一种可能的组合,再利用线段树 O ( l o g 2 N ) O(log_2N) O(log2N)求出最大连续子段和,因此总时间 O ( N 2 l o g 2 N ) O(N^2log_2N) O(N2log2N)
代码示例:

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;

const int N = 3010;
int t,n;
int tmp[N];	//离散化用
typedef long long ll;
struct Chest{
	int x,y,val;
	bool operator <(const Chest & B) const{
		return x < B.x;
	}
}cs[N];
struct SegmentTree{
	/*分别为区间和,区间最大子段和,
	从左侧开始的最大子段和,从右侧开始的最大子段和*/
	ll sum,mx,lx,rx; 
	#define sum(x) tr[x].sum
	#define mx(x) tr[x].mx
	#define lx(x) tr[x].lx
	#define rx(x) tr[x].rx 
}tr[N*4];
void BuildTree(int rt,int l,int r){
	/*在区间[l,r]上建立一棵线段树*/
	sum(rt) = mx(rt) = lx(rt) = rx(rt) = 0;
	if(l == r) return;
	int mid = l+r>>1;
	BuildTree(rt*2,l,mid);
	BuildTree(rt*2+1,mid+1,r);
}
void spread(int rt){
	/*用于根据rt的子节点更新rt的4个数据*/
	sum(rt) = sum(rt*2)+sum(rt*2+1);
	mx(rt) = max(rx(rt*2)+lx(rt*2+1),max(mx(rt*2),mx(rt*2+1)));
	lx(rt) = max(lx(rt*2),sum(rt*2)+lx(rt*2+1));
	rx(rt) = max(rx(rt*2+1),sum(rt*2+1)+rx(rt*2));
} 
void Insert(int rt,int p,int val,int l,int r){
	/*将线段树p位置的值设为val;l,r是辅助作用哦*/
	if(l == r){
		sum(rt) += val;
		lx(rt) = rx(rt) = mx(rt) = sum(rt); 
		return;
	} 
	int mid = l+r>>1;
	if(p <= mid) Insert(rt*2,p,val,l,mid);
	else Insert(rt*2+1,p,val,mid+1,r);
	spread(rt);	//在回溯时更新节点的值 
}
ll ask(){
	/*返回当前最大连续子段和*/
	return  tr[1].mx;
}
void solve(){
	sort(tmp+1,tmp+1+n);
	int cnt = unique(tmp+1,tmp+1+n)-tmp-1;
	sort(cs+1,cs+1+n);
	ll ans = 0;
	/*我们对每一个x[i]上限,枚举所有可能x[j]的下限,
		并利用线段树求出x[i]~x[j]最大字段和*/
	for(int i = 1;i <= n;i++){
		if(cs[i].x == cs[i-1].x && i != 1) continue;
		BuildTree(1,1,cnt);
		for(int j = i;j <= n;j++){
			if(cs[j].x != cs[j-1].x && i != j) ans = max(ans,ask());
			int y = lower_bound(tmp+1,tmp+1+cnt,cs[j].y)-tmp;
			Insert(1,y,cs[j].val,1,cnt);
		}
		ans = max(ans,ask());//如果全部相等,在此处特判 
	}
	printf("%lld\n",ans);
} 
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i = 1;i <= n;i++){
			scanf("%d%d%d",&cs[i].x,&cs[i].y,&cs[i].val);
			tmp[i] = cs[i].y;
		}
		solve();
	}
}

在这里插入图片描述

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:白松林 返回首页