子序列累加和+递归 求身高不下降情况下子序列分数累加和最大

78 阅读2分钟

题目

image.png

  • 从左往右遍历模型,当前要或者不要,要的条件是比之前的身高要高,然后返回所有情况中得分最高的
function process(heights, values, index, preHeight) {
  if (index === heights.length) {
    return 0;
  }

  const p1 = process(heights, values, index + 1, preHeight);
  const p2 =
    heights[index] > preHeight
      ? values[index] + process(heights, values, index + 1, heights[index])
      : 0;

  return Math.max(p1, p2);
}

  • 进阶方式:使用线段树(因为支持更新和添加),将身高映射成索引,比如150=>1,160=>2,170=>3,通过线段树存储每一个身高结尾位置的最大值,当来到当前身高位置对应的索引时,假设为4,查找1-3的最大值,假设为3,查找1-2的最大值

image.png

	public static int maxSum(int[] h, int[] v) {
		int n = h.length;
		int[] rank = new int[n];
		for (int i = 0; i < n; i++) {
			rank[i] = h[i];
		}
		Arrays.sort(rank);
		SegmentTree st = new SegmentTree(n);
		for (int i = 0; i < n; i++) {
			int height = rank(rank, h[i]);
			// 1~height max
			st.update(height, st.max(height) + v[i]);
		}
		return st.max(n);
	}

	// [150, 152, 160, 175]  160
	//   1    2    3    4
	// 3
	public static int rank(int[] rank, int num) {
		int l = 0;
		int r = rank.length - 1;
		int m = 0;
		int ans = 0;
		while (l <= r) {
			m = (l + r) / 2;
			if (rank[m] >= num) {
				ans = m;
				r = m - 1;
			} else {
				l = m + 1;
			}
		}
		return ans + 1;
	}

	public static class SegmentTree {
		private int n;
		private int[] max;
		private int[] update;

		public SegmentTree(int maxSize) {
			n = maxSize + 1;
			max = new int[n << 2];
			update = new int[n << 2];
			Arrays.fill(update, -1);
		}

		public void update(int index, int c) {
			update(index, index, c, 1, n, 1);
		}

		public int max(int right) {
			return max(1, right, 1, n, 1);
		}

		private void pushUp(int rt) {
			max[rt] = Math.max(max[rt << 1], max[rt << 1 | 1]);
		}

		private void pushDown(int rt, int ln, int rn) {
			if (update[rt] != -1) {
				update[rt << 1] = update[rt];
				max[rt << 1] = update[rt];
				update[rt << 1 | 1] = update[rt];
				max[rt << 1 | 1] = update[rt];
				update[rt] = -1;
			}
		}

		private void update(int L, int R, int C, int l, int r, int rt) {
			if (L <= l && r <= R) {
				max[rt] = C;
				update[rt] = C;
				return;
			}
			int mid = (l + r) >> 1;
			pushDown(rt, mid - l + 1, r - mid);
			if (L <= mid) {
				update(L, R, C, l, mid, rt << 1);
			}
			if (R > mid) {
				update(L, R, C, mid + 1, r, rt << 1 | 1);
			}
			pushUp(rt);
		}

		private int max(int L, int R, int l, int r, int rt) {
			if (L <= l && r <= R) {
				return max[rt];
			}
			int mid = (l + r) >> 1;
			pushDown(rt, mid - l + 1, r - mid);
			int ans = 0;
			if (L <= mid) {
				ans = Math.max(ans, max(L, R, l, mid, rt << 1));
			}
			if (R > mid) {
				ans = Math.max(ans, max(L, R, mid + 1, r, rt << 1 | 1));
			}
			return ans;
		}

	}

	// 为了测试
	public static int[] randomArray(int n, int v) {
		int[] ans = new int[n];
		for (int i = 0; i < n; i++) {
			ans[i] = (int) (Math.random() * v) + 1;
		}
		return ans;
	}

	// 为了测试
	public static void main(String[] args) {
		int N = 30;
		int V = 100;
		int testTime = 20000;
		System.out.println("测试开始");
		for (int i = 0; i < testTime; i++) {
			int n = (int) (Math.random() * N) + 1;
			int[] h = randomArray(n, V);
			int[] v = randomArray(n, V);
			if (right(h, v) != maxSum(h, v)) {
				System.out.println("出错了!");
			}
		}
		System.out.println("测试结束");
	}