【ccpc网络赛】YJJ's Salesman【1010】【树状数组+离散化+dp】

题意:

      一个正方形地图上给定N个点,从 (0,0) 开始走向 (1e9,1e9),地图中每个点都有一个权值,只有从 (x-1,y-1)到达 (x,y)这个点,(x,y)这个点的权值才能够获得。要求走到终点时所获得的权值最大。

思路:

     将点的 y 值离散化到数组 a 上,再将所有点按照 x 进行排序,从第一列遍历到最后一列,对于第 i 列上的每一个点,求出 (1 , i-1) 列上 dp 的最大值,用树状数组进行记录。

     更新dp[ i ]的同时对 ans 取最大值,最后ans的值就是答案。

代码:

#include <bits/stdc++.h>
using namespace std;

struct Node {
	int x, y, v;
	void input() {
		scanf("%d%d%d", &x ,&y, &v);
	}
}node[100010];
bool cmp(Node a, Node b) {
	return a.x < b.x;
}

int a[100010];
int tot;

int c[100010];	//树状数组,c[i]表示【i-lowbit(i)+1,i】这个区间维护的值
int lowbit(int x) {
	return x&(-x);
}

void update(int i, int val) {
	while (i <= tot) {
		c[i] = max(c[i], val);
		i += lowbit(i);
	}
}

int query(int i) {	//求出【1,i】区间维护的内容,可以求sum,也可以求max/min
	int res = 0;
	while (i > 0) {
		res = max(res, c[i]); //维护最大值
		i -= lowbit(i);
	}
	return res;
}

int dp[100010];


int main() {
	int T;
	int n;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		//读入点
		for (int i = 0; i < n; i++)
			node[i].input();
		//将y读入数组a,进行离散化
		tot = 0;
		for (int i = 0; i < n; i++) {
			a[tot++] = node[i].y;
		}
		sort(a, a+tot);
		//离散化后进行去重
		tot = unique(a, a+tot) - a;
		//排序后再将标号传回去
		for (int i = 0; i < n; i++) {
			node[i].y = lower_bound(a, a+tot, node[i].y) - a + 1;
		}

		//对x坐标排序
		sort(node, node+n, cmp);
		for (int i = 0; i < n; i++) dp[i] = node[i].v;
		
		//c[i]是树状数组,c[i]表示【i-lowbit(i)+1,i】这个区间,长度为lowbit(i)
		for (int i = 1; i <= tot; i++)c[i] = 0;
		int pos = 0;
		int ans = 0;
		
		//从第一列开始往之后的列更新
		//对于每个点求该点之前的最大值进行更新
		for (int i = 0; i < n; i++) {
			while (pos < i && node[pos].x < node[i].x) {
				update(node[pos].y, dp[pos]);
				pos++;
			}
			dp[i] = query(node[i].y - 1) + node[i].v;
			ans = max(ans, dp[i]);
		}
		printf("%d\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/82055342