1. 房屋偷盗
一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响小偷偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组 nums
,请计算 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例
示例 1:
输入:nums = [1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:nums = [2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
代码
int rob(vector<int>& nums) {
if(nums.size()==1) return nums[0];
vector<int> dp(nums.size());
dp[0]=nums[0], dp[1]=max(nums[0],nums[1]);
for(int i=2;i<dp.size();i++){
dp[i]=max(dp[i-1],nums[i]+dp[i-2]);
}
return dp.back();
}
2. 环形房屋偷盗
一个专业的小偷,计划偷窃一个环形街道上沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组 nums
,请计算 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
示例
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
代码
int rob(vector<int> &nums) {
int n = nums.size(), result;
if (n == 1) return nums[0];
if (n == 2) return max(nums[0], nums[1]);
vector<int> dp(n);
dp[0] = nums[0], dp[1] = max(nums[0], nums[1]); // 不偷最后一家
for (int i = 2; i < n - 1; i++) {
dp[i] = max(nums[i] + dp[i - 2], dp[i - 1]);
}
result = dp[n - 2];
dp[1] = nums[1], dp[2] = max(nums[1], nums[2]); // 偷最后一家
for (int i = 3; i < n; i++) {
dp[i] = max(nums[i] + dp[i - 2], dp[i - 1]);
}
return max(result, dp[n - 1]);
}
3. 爬楼梯的最少成本
数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。
每当爬上一个阶梯都要花费对应的体力值,一旦支付了相应的体力值,就可以选择向上爬一个阶梯或者爬两个阶梯。
请找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。
示例
示例 1:
输入:cost = [10, 15, 20]
输出:15
解释:最低花费是从 cost[1] 开始,然后走两步即可到阶梯顶,一共花费 15 。
示例 2:
输入:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出:6
解释:最低花费方式是从 cost[0] 开始,逐个经过那些 1 ,跳过 cost[3] ,一共花费 6 。
代码
int minCostClimbingStairs(vector<int> &cost) {
vector<int> dp(cost.size() + 1, 0);
for (int i = 2; i < dp.size(); i++) {
dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
}
return dp.back();
}
4. 推箱子 (something wrong)
「推箱子」是一款风靡全球的益智小游戏,玩家需要将箱子推到仓库中的目标位置。
游戏地图用大小为 m x n
的网格 grid
表示,其中每个元素可以是墙、地板或者是箱子。
现在你将作为玩家参与游戏,按规则将箱子 'B'
移动到目标位置 'T'
:
玩家用字符 'S'
表示,只要他在地板上,就可以在网格中向上、下、左、右四个方向移动。
地板用字符 '.'
表示,意味着可以自由行走。
墙用字符 ‘#’ 表示,意味着障碍物,不能通行。
箱子仅有一个,用字符 'B'
表示。相应地,网格上有一个目标位置 'T'
。
玩家需要站在箱子旁边,然后沿着箱子的方向进行移动,此时箱子会被移动到相邻的地板单元格。记作一次「推动」。
玩家无法越过箱子。
返回将箱子推到目标位置的最小 推动 次数,如果无法做到,请返回 -1
。
示例
示例 1:
输入:grid = [["#","#","#","#","#","#"],
["#","T","#","#","#","#"],
["#",".",".","B",".","#"],
["#",".","#","#",".","#"],
["#",".",".",".","S","#"],
["#","#","#","#","#","#"]]
输出:3
解释:我们只需要返回推箱子的次数。
示例 2:
输入:grid = [["#","#","#","#","#","#"],
["#","T","#","#","#","#"],
["#",".",".","B",".","#"],
["#","#","#","#",".","#"],
["#",".",".",".","S","#"],
["#","#","#","#","#","#"]]
输出:-1
示例 3:
输入:grid = [["#","#","#","#","#","#"],
["#","T",".",".","#","#"],
["#",".","#","B",".","#"],
["#",".",".",".",".","#"],
["#",".",".",".","S","#"],
["#","#","#","#","#","#"]]
输出:5
解释:向下、向左、向左、向上再向上。
提示
m == grid.length
n == grid[i].length
1 <= m, n <= 20
grid
仅包含字符'.'
,'#'
,'S'
,'T'
, 以及'B'
。grid
中'S'
,'B'
和'T'
各只能出现一个。
代码1 (something wrong)
以下示例答案为
8
,输出结果为INT_MAX
,代码待修正。
#include <bits/stdc++.h>
#define N 20
#pragma GCC optimize(2)
int xMax, yMax, xTarget, yTarget, minMove;
using namespace std;
const int DIR[4][2]{{-1, 0},
{1, 0},
{0, -1},
{0, 1}};
bool visited[N][N][4]{false};
bool isObstacle(const vector<vector<char>> &grid, int x, int y) {
return x < 0 || y < 0 || x >= xMax || y >= yMax || grid[x][y] != '.';
}
bool isAccessible(vector<vector<char>> &grid, int xStart, int yStart, int xEnd, int yEnd) {
if (isObstacle(grid, xEnd, yEnd)) { return false; }
if (xStart == xEnd && yStart == yEnd) { return true; }
for (int i = 0, dx, dy; i < 4; i++) {
dx = DIR[i][0], dy = DIR[i][1];
if (!isObstacle(grid, xStart + dx, yStart + dy)) {
char prev = grid[xStart][yStart];
grid[xStart][yStart] = 'V';
if (isAccessible(grid, xStart + dx, yStart + dy, xEnd, yEnd)) {
grid[xStart][yStart] = prev;
return true;
}
grid[xStart][yStart] = prev;
}
}
return false;
}
tuple<int, int, int, int> convertGrid(vector<vector<char>> &grid) {
int xHuman, yHuman, xBox, yBox;
for (int i = 0; i < xMax; i++) {
for (int j = 0; j < yMax; j++) {
if (grid[i][j] == 'B') { xBox = i, yBox = j; }
if (grid[i][j] == 'S') { xHuman = i, yHuman = j, grid[i][j] = '.'; }
if (grid[i][j] == 'T') { xTarget = i, yTarget = j, grid[i][j] = '.'; }
}
}
return tuple<int, int, int, int>{xHuman, yHuman, xBox, yBox};
}
void bfs(vector<vector<char>> &grid, int nMove, int iBox, int jBox, int iHuman, int jHuman) {
if (iBox == xTarget && jBox == yTarget) {
minMove = min(minMove, nMove);
return;
}
for (int i = 0, dx, dy; i < 4; i++) {
if (visited[iBox][jBox][i]) continue; // 状态已访问
visited[iBox][jBox][i] = true; // 访问该状态
dx = DIR[i][0], dy = DIR[i][1];
if (isObstacle(grid, iBox + dx, jBox + dy)) continue; // 下一位置为障碍点
if (!isAccessible(grid, iHuman, jHuman, iBox - dx, jBox - dy)) continue; // 推动箱子的位置不可到达
grid[iBox][jBox] = '.', grid[iBox + dx][jBox + dy] = 'B';
bfs(grid, nMove + 1, iBox + dx, jBox + dy, iBox - dx, jBox - dy);
grid[iBox][jBox] = 'B', grid[iBox + dx][jBox + dy] = '.';
}
}
int main() {
vector<vector<char>> grid{{'#', '.', '.', '#', 'T', '#', '#', '#', '#'},
{'#', '.', '.', '#', '.', '#', '.', '.', '#'},
{'#', '.', '.', '#', '.', '#', 'B', '.', '#'},
{'#', '.', '.', '.', '.', '.', '.', '.', '#'},
{'#', '.', '.', '.', '.', '#', '.', 'S', '#'},
{'#', '.', '.', '#', '.', '#', '#', '#', '#'}};
xMax = grid.size(), yMax = grid[0].size();
int xHuman, yHuman, xBox, yBox;
minMove = INT_MAX;
tie(xHuman, yHuman, xBox, yBox) = convertGrid(grid);
bfs(grid, 0, xBox, yBox, xHuman, yHuman);
cout << minMove;
return 0;
}
代码2 (超时)
#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int step[1 << 20];
int n, m;
int dir[4][2]{{0, 1},
{1, 0},
{0, -1},
{-1, 0}};
bool isValid(const vector<vector<char>> &grid, int x, int y) {
return x >= 0 && x < m && y >= 0 && y < n && grid[x][y] != '#';
}
int pack(int xStart, int yStart, int xBox, int yBox) {
return (xStart << 15) | (yStart << 10) | (xBox << 5) | yBox;
}
tuple<int, int, int, int> unPack(int val) {
return {val >> 15 & 31, val >> 10 & 31, val >> 5 & 31, val & 31};
}
int minPushBox(vector<vector<char>> &grid) {
m = grid.size(), n = grid[0].size();
memset(step, -1, sizeof(step));
int xStart, yStart, xBox, yBox;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == 'S') { xStart = i, yStart = j, grid[i][j] = '.'; }
if (grid[i][j] == 'B') { xBox = i, yBox = j, grid[i][j] = '.'; }
}
}
queue<int> que;
int packValue = pack(xStart, yStart, xBox, yBox);
int ans = -1;
int xStartNext, yStartNext, xBoxNext, yBoxNext, packValueNext;
step[packValue] = 0;
que.push(packValue);
while (!que.empty()) {
packValue = que.front();
que.pop();
tie(xStart, yStart, xBox, yBox) = unPack(packValue);
if (grid[xBox][yBox] == 'T') {
if (ans == -1 || step[packValue] < ans) ans = step[packValue];
continue;
}
for (auto &i : dir) {
xStartNext = xStart + i[0];
yStartNext = yStart + i[1];
if (!isValid(grid, xStartNext, yStartNext)) continue;
if (xStartNext == xBox && yStartNext == yBox) {
xBoxNext = xBox + i[0];
yBoxNext = yBox + i[1];
if (!isValid(grid, xBoxNext, yBoxNext)) continue;
packValueNext = pack(xStartNext, yStartNext, xBoxNext, yBoxNext);
if (step[packValueNext] == -1 || step[packValueNext] + 1 < step[packValue]) {
step[packValueNext] = step[packValue] + 1;
que.push(packValueNext);
}
} else {
packValueNext = pack(xStartNext, yStartNext, xBox, yBox);
if (step[packValueNext] == -1 || step[packValueNext] < step[packValue]) {
step[packValueNext] = step[packValue];
que.push(packValueNext);
}
}
}
}
return ans;
}
int main() {
vector<vector<char>> grid{{'.', '.', '#', '.', '.', '.', '.', '#'},
{'.', 'B', '.', '.', '.', '.', '.', '#'},
{'.', '.', 'S', '.', '.', '.', '.', '.'},
{'.', '#', '.', '.', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '.', '.', '.'},
{'.', '.', '.', 'T', '.', '.', '.', '.'},
{'.', '.', '.', '.', '.', '.', '.', '#'},
{'.', '#', '.', '.', '.', '.', '.', '.'}};
cout << minPushBox(grid);
return 0;
}