很多时候我们都会遇到一些题目,给出很多个位置,在这个位置进行操作或者不进行操作,我们称之为选与不选问题
如果我们进行dfs进行枚举的话,复杂度可能吃不消,我们借鉴背包的思路,背包的思路本质就是枚举每一个物品选与不选
这个题目我的定义就是 dp[ i ] [ j ] 为使用了i块地毯,到第 j 个位置能够覆盖的白色区域的最大数量,我省略了第一个维度的空间。
我们提前处理处理好白色数量的前缀和pre
注意的是,我们的转移方程是
d p [ j ] = m a x ( d p [ u ] 1 < = u < = ( j − c a r p e t L e n ) + p r e [ j ] − p r e [ j − c a r p e t L e n ] , d p [ j ] ) dp [ j ] = max( dp[ u ]_{1<= u <=(j-carpetLen)} + pre[j] - pre[j-carpetLen] , dp[ j ] ) dp[j]=max(dp[u]1<=u<=(j−carpetLen)+pre[j]−pre[j−carpetLen],dp[j])
现在有几个难点就是如何快速算出 j 前面的最大值,以及每次算完后如何更新整个的答案区间呢
for(int j=1;j<=n;j++){
c[j] = max(c[j-1],dp[j]); // 通过迭代的方式求出前面的最小值
}
for(int j=1;j<=n;j++){
dp[j] = max(dp[j],dp[j-1]); // 每次算完更新一下
}
完整的代码如下:
class Solution {
public:
int minimumWhiteTiles(string floor, int numCarpets, int carpetLen) {
int n = floor.size();
int ans = 0x3f3f3f3f;
vector<int> pre(n+1,0);
for(int i=0;i<n;i++){
pre[i+1] = pre[i];
if(floor[i]=='1'){
pre[i+1] += 1;
}
}
// 已经处理好了前缀和
// 开始枚举
vector<int> dp(n+1,0); // dp[i][j] 表示用i块地毯到第j个位置的覆盖的白色方块最大数
// 先进行初始化
int big = 0;
for(int i = 1;i<=n;i++){
if(i<=carpetLen){
dp[i] = pre[i];
big = max(big,dp[i]);
}else{
dp[i] = max(big,pre[i]-pre[i-carpetLen]);
big = max(big,dp[i]);
}
}
for(int i=2;i<=numCarpets;i++){
// 枚举每一个位置
// 提前找出每一个位置包含这个位置的的最大值
vector<int> c(n+1,0);
for(int j=1;j<=n;j++){
c[j] = max(c[j-1],dp[j]);
}
for(int j=n;j>=(carpetLen);j--){
dp[j] = max(c[j-carpetLen]+pre[j]-pre[j-carpetLen],dp[j]);
}
for(int j=1;j<=n;j++){
dp[j] = max(dp[j],dp[j-1]);
}
}
return pre[n] - dp[n];
}
};