动态规划+RMQ 商品返回最大值

76 阅读1分钟

题目

给定N件物品,每个物品有重量(w[i])、有价值(v[i]),只能最多选两件商品,重量不超过bag,返回价值最大能是多少?

  • 动态规划思想:从左往右遍历模型,当前要或者不要
function process(w, v, index, restNumber, restWeight) {
  if (restNumber < 0 || restWeight < 0) {
    return -1;
  }
  // 越界
  if (index === w.length) {
    return 0;
  }
  let p1 = process(w, v, index + 1, restNumber, restWeight);
  let p2;
  let res = process(w, v, index + 1, restNumber - 1, restWeight - w[index]);
  // 要当前value
  if (res != -1) {
    p2 = v[index] + res;
  }

  return Math.max(p1, p2);
}

  • RMQ方式
  • 首先对整个数组按照重量进行排序,然后从左往右遍历,假设当前位置重量为n,总重量为m,就是要找重量<=m-n里,价值最大的,查找<=m-n的左边通过二分查找最右的那个,每个位置都遍历一遍就能得出答案
  • 查找到最右边界后,相当于知晓了范围,通过RMQ去计算范围内的value最大值,i位置在RMQ中对应i+1,RMQ
	public static int max2(int[] w, int[] v, int bag) {
		int n = w.length;
		int[][] arr = new int[n][2];
		for (int i = 0; i < n; i++) {
			arr[i][0] = w[i];
			arr[i][1] = v[i];
		}
		// O(N * logN)
		Arrays.sort(arr, (a, b) -> (a[0] - b[0]));
		// 重量从轻到重,依次标号1、2、3、4....
		// 价值依次被构建成了RMQ结构
		// O(N * logN)
		RMQ rmq = new RMQ(arr);
		int ans = 0;
		// N * logN
           // j 就是 RMQ 中的索引
		for (int i = 0, j = 1; i < n && arr[i][0] <= bag; i++, j++) {
			// 当前来到0号货物,RMQ结构1号
			// 当前来到i号货物,RMQ结构i+1号
			// 查询重量的边界,重量 边界 <= bag - 当前货物的重量
			// 货物数组中,找到 <= 边界,最右的位置i
			// RMQ,位置 i + 1
			int right = right(arr, bag - arr[i][0]) + 1;
			int rest = 0;
			// 查找到的最右边界就是自身
			if (right == j) {
				rest = rmq.max(1, right - 1);
			} else if (right < j) {
                        // 查找到的最右边界在自身位置左边
				rest = rmq.max(1, right);
			} else { // right > j
                        // 查找到的最右边界在自身位置右边就要分两段查找,因为不能包含自身
                        // 左边到自身-1,自身+1到右边的范围分别查找value最大值然后比较
				rest = Math.max(rmq.max(1, j - 1), rmq.max(j + 1, right));
			}
			ans = Math.max(ans, arr[i][1] + rest);
		}
		return ans;
	}
        
    public static int right(int[][] arr, int limit) {
		int l = 0;
		int r = arr.length - 1;
		int m = 0;
		int ans = -1;
		while (l <= r) {
			m = (l + r) / 2;
			if (arr[m][0] <= limit) {
				ans = m;
				l = m + 1;
			} else {
				r = m - 1;
			}
		}
		return ans;
	}
        
        
	public static class RMQ {
		public int[][] max;

		public RMQ(int[][] arr) {
			int n = arr.length;
			int k = power2(n);
			max = new int[n + 1][k + 1];
			for (int i = 1; i <= n; i++) {
				max[i][0] = arr[i - 1][1];
			}
			for (int j = 1; (1 << j) <= n; j++) {
				for (int i = 1; i + (1 << j) - 1 <= n; i++) {
					max[i][j] = Math.max(max[i][j - 1], max[i + (1 << (j - 1))][j - 1]);
				}
			}
		}

		public int max(int l, int r) {
			if (r < l) {
				return 0;
			}
			int k = power2(r - l + 1);
			return Math.max(max[l][k], max[r - (1 << k) + 1][k]);
		}

		private int power2(int m) {
			int ans = 0;
			while ((1 << ans) <= (m >> 1)) {
				ans++;
			}
			return ans;
		}

	}