#P2292. 第2题-俄罗斯方块
          
                        
                                    
                      
        
              - 
          
          
                      2000ms
            
          
                      Tried: 1276
            Accepted: 186
            Difficulty: 5
            
          
          
          
                       所属公司 : 
                              华为
                                
            
                        
              时间 :2024年9月25日-国内
                              
                      
          
 
- 
                        算法标签>DFS          
 
第2题-俄罗斯方块
题面解释:
在一个n×n的网格游戏中,可以放置由4个小方块组成的大方块(类似俄罗斯方块)。网格中有k个位置不能放置方块,这些位置通过坐标给出。我们需要计算在不重叠、不超出边界的情况下,最多可以放置多少个大方块。输入的第一行包含n和k,接下来有k行给出不能放置方块的坐标对(y,x)。输出结果为最多能放下的大方块数量。举例来说,对于输入2 0,输出为1;对于输入4 3,输出为2;而输入3 3则输出为0。
思路:DFS暴力回溯
本题非常类似于:LeetCode 1240. 铺瓷砖。
采用深度优先搜索(DFS)和回溯法来解决大方块的摆放问题。我们通过递归遍历每个位置,判断是否可以放置一个 2x2 的大方块。
分两种情况:1.如果可以放置,就标记位置并继续探索下一个位置;如果不行,直接跳过。每次成功放置时更新最大值,最后回溯到上一步,取消放置状态。
题解
在本题中,我们需要在一个 n×n 的网格中放置尽可能多的 2×2 大方块,网格中有一些位置被标记为障碍物,不能放置大方块。为了解决这个问题,我们使用了深度优先搜索(DFS)和回溯法的策略。
具体步骤如下:
- 递归遍历网格:从左上角开始,逐行逐列地检查每个位置是否可以放置一个 2×2 的大方块。
 - 判断放置条件:在检查当前格子时,我们需要确保这个 2×2 的区域内没有障碍物且没有超出网格边界。
 - 回溯:如果能够放置大方块,就将这4个位置标记为已占用,继续递归搜索下一个位置;如果不行,则跳过当前格子,继续尝试下一个位置。搜索完成后,恢复当前格子的状态,准备回溯到上一个状态。
 - 更新结果:每次成功放置一个大方块时,更新最大放置数量。
 
通过这种方法,我们可以有效地探索所有可能的放置方案,最终得到最多可以放置的大方块数量。
代码
java
import java.util.Scanner;
public class Main {
    static int n, k;
    static int[][] vis = new int[105][105];
    static int[] dx = {-1, -1, 0, 0}; // 移动方向数组
    static int[] dy = {-1, 0, -1, 0}; // 移动方向数组
    // 深度优先搜索
    static int dfs(int x, int y) {
        if (x >= n) return 0; // 递归边界
        boolean canPlace = true;
        for (int i = 0; i < 4; i++) {
            if (vis[x + dx[i]][y + dy[i]] == 1) canPlace = false;
        }
        int res = 0;
        if (canPlace) {
            // 标记格子为已访问
            for (int i = 0; i < 4; i++) {
                vis[x + dx[i]][y + dy[i]] = 1;
            }
            if (y < n - 1)
                res = Math.max(res, dfs(x, y + 1) + 1);
            else
                res = Math.max(res, dfs(x + 1, 1) + 1);
            // 回溯,重置访问状态
            for (int i = 0; i < 4; i++) {
                vis[x + dx[i]][y + dy[i]] = 0;
            }
        }
        // 继续尝试下一个位置
        if (y < n - 1)
            res = Math.max(res, dfs(x, y + 1));
        else
            res = Math.max(res, dfs(x + 1, 1));
        return res;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        k = sc.nextInt();
        
        for (int i = 1; i <= k; i++) {
            int x = sc.nextInt();
            int y = sc.nextInt();
            vis[x][y] = 1; // 读取障碍物的位置
        }
        System.out.println(dfs(1, 1)); // 从 (1, 1) 开始搜索
        sc.close();
    }
}
python
n, k = map(int, input().split())
vis = [[0] * 105 for _ in range(105)]
dx = [-1, -1, 0, 0]  # 移动方向数组
dy = [-1, 0, -1, 0]  # 移动方向数组
# 深度优先搜索
def dfs(x, y):
    if x >= n:
        return 0  # 递归边界
    can_place = True
    for i in range(4):
        if vis[x + dx[i]][y + dy[i]] == 1:
            can_place = False
    res = 0
    if can_place:
        # 标记格子为已访问
        for i in range(4):
            vis[x + dx[i]][y + dy[i]] = 1
        if y < n - 1:
            res = max(res, dfs(x, y + 1) + 1)
        else:
            res = max(res, dfs(x + 1, 1) + 1)
        # 回溯,重置访问状态
        for i in range(4):
            vis[x + dx[i]][y + dy[i]] = 0
    # 继续尝试下一个位置
    if y < n - 1:
        res = max(res, dfs(x, y + 1))
    else:
        res = max(res, dfs(x + 1, 1))
    return res
# 读取障碍物的位置
for _ in range(k):
    x, y = map(int, input().split())
    vis[x][y] = 1
# 从 (1, 1) 开始搜索
print(dfs(1, 1))
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long // 定义int为long long类型,避免数据溢出
int n, k; // 网格的边长和障碍物的数量
int vis[105][105]; // 访问状态数组,记录哪些格子已被占用
int dx[4] = {-1, -1, 0, 0}; // 2x2方块的行偏移量
int dy[4] = {-1, 0, -1, 0}; // 2x2方块的列偏移量
// 深度优先搜索函数
int dfs(int x, int y) {
    if (x >= n) return 0; // 递归边界:如果行数超出网格,返回0
    // 检查当前位置是否可以放置大方块
    int canPlace = 1; // 标记当前方块是否可以放置
    for (int i = 0; i < 4; i++) {
        if (vis[x + dx[i]][y + dy[i]] == 1) canPlace = 0; // 检查四个小方块位置是否已被占用
    }
    int res = 0; // 记录当前状态下可以放置的大方块数量
    if (canPlace == 1) {
        // 如果可以放置,标记这4个格子为已占用
        for (int i = 0; i < 4; i++) {
            vis[x + dx[i]][y + dy[i]] = 1;
        }
        // 递归继续探索下一个位置
        if (y < n - 1)
            res = max(res, dfs(x, y + 1) + 1); // 尝试向右移动
        else
            res = max(res, dfs(x + 1, 0) + 1); // 尝试向下移动
        // 回溯,重置访问状态
        for (int i = 0; i < 4; i++) {
            vis[x + dx[i]][y + dy[i]] = 0;
        }
    }
    // 继续尝试下一个位置(不放置当前方块)
    if (y < n - 1)
        res = max(res, dfs(x, y + 1)); // 尝试向右移动
    else
        res = max(res, dfs(x + 1, 0)); // 尝试向下移动
    return res; // 返回最大可以放置的大方块数量
}
signed main() {
    cin >> n >> k; // 输入网格的边长和障碍物数量
    for (int i = 1; i <= k; i++) {
        int x, y;
        cin >> x >> y; // 读取每个障碍物的位置
        vis[x][y] = 1; // 标记障碍物的位置为已占用
    }
    cout << dfs(0, 0); // 从 (0, 0) 开始进行搜索
    return 0; // 程序结束
}
OJ会员可以通过点击题目上方《已通过》查看其他通过代码来学习。
题目内容
在俄罗斯方块游戏中,只有下面1种大方块,由四个正方形小方块组成。现在,请计算在给定网格大小的情况下,最多可以放置多少个大方块。 具体规则如下:
1、网格为正方形网络。
2、方块不能重叠。
3、方块不能超出网格的边界。
4、网格中部分位置不能放置方块。

输入描述
n k
y1 x1
y2 x2
...
yk xk
表示边长为n的正方形网格,有k个位置不能放置方块,接下来k行坐标对,y表示自上向下的第几行,x表示自左向右的第几列(坐标从0开始编号,左上角为0 0)。
n的范围: [1,8]
k的范围:[0,64]
x、y的范围: [0,n)
输出描述
最多能放下多少大方块。
样例1
输入
2 0
输出
1
说明
只能放下1个
样例2
输入
4 3
1 0
1 3
2 1		
输出
2
说明
最多放2个大方块,如下图

样例3
输入
3 3
0 1
1 2
2 0
输出
0
说明
没有位置可以放置方块。
