740. 删除并获得点数
给你一个整数数组 nums ,你可以对它进行一些操作。
每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。
开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。
链接: 740. 删除并获得点数
示例 1:
输入:nums = [3,4,2]
输出:6
解释:
删除 4 获得 4 个点数,因此 3 也被删除。
之后,删除 2 获得 2 个点数。总共获得 6 个点数。
示例 2:
输入:nums = [2,2,3,3,3,4]
输出:9
解释:
删除 3 获得 3 个点数,接着要删除两个 2 和 4 。
之后,再次删除 3 获得 3 个点数,再次删除 3 获得 3 个点数。
总共获得 9 个点数。
解法思路
从题目可以看出,本题和之前的 打家劫舍 系列题目有些类似,选取 i 后
i-1 和 i+1 都是不能选择的,但是该题我们无法直接进行动态规划,需要对数据进行预处理。
因此,我们可以创建⼀个⼤⼩为 10001 (根据题⽬的数据范围)的 arr 数组,将 nums 数组中每⼀个元素 x ,累加到 arr 数组下标为 x 的位置处,然后在 arr 数组上来⼀次「打家劫舍」即可。
代码:
int deleteAndEarn(vector<int>& nums) {
int N=10001;
//预处理部分
vector<int> arr(N);
for(auto e:nums)
{
arr[e]+=e;
}
//动态规划解题部分
vector<int> f(N),g(N);
f[0]=arr[0];f[1]=arr[1];
g[0]=0;g[1]=arr[0];
for(int i=2;i<N;i++)
{
f[i]=g[i-1]+arr[i];
g[i]=max(f[i-1],g[i-1]);
}
return max(f[N-1],g[N-1]);
}
LCR 091. 粉刷房子
假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。
当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。
例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。
请计算出粉刷完所有房子最少的花费成本。
链接: LCR 091. 粉刷房子
示例 1:
输入: costs = [[17,2,17],[16,16,5],[14,3,19]]
输出: 10
解释: 将 0 号房子粉刷成蓝色,1 号房子粉刷成绿色,2 号房子粉刷成蓝色。
最少花费: 2 + 5 + 3 = 10。
示例 2:
输入: costs = [[7,6,2]]
输出: 2
1.状态表示
对于这种「路径类」的问题,我们的状态表⽰⼀般有两种形式:
- i. 从 [i, j] 位置出发,……;
- ii. 从起始位置出发,到达 [i, j] 位置,……;
这⾥我们选择⽐较常⽤的⽅式,以某个位置为结尾,结合题⽬要求,定义⼀个状态表⽰:
可以看出,以某个位置为结尾时,结尾处会有三种状态,分别对应着三种颜色,
所以我们定义一个二维数组:
- dp[i][0] 表⽰:粉刷到 i 位置的时候,最后⼀个位置粉刷上「红⾊」,此时的最⼩花费;
- dp[i][1] 表⽰:粉刷到 i 位置的时候,最后⼀个位置粉刷上「蓝⾊」,此时的最⼩花 费;
- ▪ dp[i][2] 表⽰:粉刷到 i 位置的时候,最后⼀个位置粉刷上「绿⾊」,此时的最⼩花 费。
2.状态转移方程
对于 dp[i][0] :
如果第i个位置粉刷上「红⾊」,那么 i-1 位置上可以是「蓝⾊」或者「绿⾊」。因此我们需要知道粉刷到i-1位置上的时候,粉刷上「蓝⾊」或者「绿⾊」的最⼩花费,然后加上i位置的花费即可。于是状态转移⽅程为:
dp[i][0] = min(dp[i - 1][1], dp[i- 1][2]) + costs[i - 1][0] ;
同理,可以推出
dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]) + costs[i - 1][1] ;
dp[i][2] = min(dp[i - 1][0], dp[i - 1][1]) + costs[i - 1][2]
3. 初始化
由状态转移方程可知,当 i 为0时,i-1不存在于数组下标中,所以需要对三个
位置进行初始化
dp[0][0]=costs[0][0];
dp[1][0]=costs[0][1];
dp[2][0]=costs[0][2];
4. 填表顺序
根据「状态转移⽅程」得「从左往右,三个表⼀起填」。
5. 返回值
根据「状态表⽰」,应该返回最后⼀个位置粉刷上三种颜⾊情况下的最⼩值,因此需要返回:
min(dp[n--1][0], min(dp[n-1][1], dp[n-1][2]))
代码:
int minCost(vector<vector<int>>& costs) {
int n=costs.size();
vector<vector<int>> dp(3,vector<int>(n));
dp[0][0]=costs[0][0];
dp[1][0]=costs[0][1];
dp[2][0]=costs[0][2];
for(int i=1;i<n;i++)
{
dp[0][i]=min(dp[1][i-1],dp[2][i-1])+costs[i][0];
dp[1][i]=min(dp[0][i-1],dp[2][i-1])+costs[i][1];
dp[2][i]=min(dp[1][i-1],dp[0][i-1])+costs[i][2];
}
return min(dp[0][n-1],min(dp[1][n-1],dp[2][n-1]));
}