解题思想
任取一个点做第一个点,计算被该点分开的两边的loss。这个点的loss加上两边loss中的最大值,就可以完全猜出两边的所有值。所以,遍历一遍所有点,得出以每个点为起点的时候,想要猜出两边所有的值的loss,然后找出最小值就可以了。 而最小问题有两个,一个是只有一个值的时候,那就返回0;另外一个是,有两个值的时候,返回左一值。
实现
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int return_max(vector<int> nums, int left, int right)
{
int res = INT_MAX;
if (left >= right) {
return 0;
}
if(left == right - 1) return nums[left];
if(left == right - 2) return nums[left + 1];
for(int i = left; i <= right; i++)
{
int left_val = return_max(nums, left, i - 1);
int right_val = return_max(nums, i + 1, right);
int cur =nums[i] + (left_val > right_val ? left_val : right_val);
res = res < cur ? res : cur;
}
return res;
}
int main()
{
int n = 33;
vector<int> nums;
nums.push_back(0);
for(int i = 1; i <= n; i++)
{
nums.push_back(i);
}
cout<<return_max(nums, 1, n)<<endl;
return 0;
}
下面这种解法非常耗时。每次计算一个left与right之间的值,它的子问题都要计算一遍。而这些子问题其实只需要计算一遍就可以了。下面加一个数组。把相同left与right之间的计算记录下来。
改进版
class Solution {
public:
int getMoneyAmount(int n) {
vector<vector<int>> record(n + 1, vector<int>(n + 1, -1));
return return_max_dy(1, n, record);
}
int return_max_dy(int left, int right, vector<vector<int>>& record)
{
int res = INT_MAX;
if (left >= right) {
return 0;
}
//if(left == right - 4) return left + 1 + left + 3;
//if(left == right - 3) return left + left + 2;
//if(left == right - 2) return left + 1;
if(left == right - 1) return left;
if(record[left][right] != -1) return record[left][right];
for(int i = left; i <= right; i++)
{
int left_val = return_max_dy(left, i - 1, record);
int right_val = return_max_dy(i + 1, right, record);
if(i - 1 >= left)
record[left][i - 1] = left_val;
if(i + 1 <= right)
record[i + 1][right] = right_val;
int cur = i + (left_val > right_val ? left_val : right_val);
res = res < cur ? res : cur;
}
return res;
}
};
个人感受
这类问题,首先要想清楚它要求的值有哪些,哪些是最优的。然后就是,如何将问题分解成子问题。最后再想清楚最小子问题有哪些。