机试题——园区规划

题目描述

园区打算购买一块 m×n 大小的土地,用于建设新园区。希望将这块土地规划为尽可能少的正方形区域。规划完这块土地后,应该满足一个状态:没有任何一个正方形能放入。

输入描述

输入第一行为一个整数 m。

输入第二行为一个整数 n。

其中 1 ≤ m ≤ 13,1 ≤ n ≤ 13。

输出描述

输出为一个整数,表示最少能规划块区域的数量。

用例输入

3
2
3

最少能规划块3个区域。 先规划1个2×2,剩下区域规划2个 1×1。

13
11
6

解题思路

本题要求使用搜索+剪枝策略解决一个NP问题,即在给定的m×n大小的土地上规划尽可能少的正方形区域。在这里采用搜索+剪枝的方法来求解。这种方法虽然可能无法保证找到最优解,但在实际比赛中可以作为一种有效的策略来尽可能拿到分数。

搜索策略

  1. 定义状态:使用一个二维数组g[i][j]来表示土地上的点是否已经被分割为了一个正方形,false表示未分割,true表示已分割。

  2. 搜索过程:从左上角开始,找到一个未被分割的点(即g[i][j] = false),尝试以该点为正方形的左上角点分割正方形。

  3. 枚举正方形边长:尝试分割不同边长的正方形,从大到小枚举,以便尽早找到一个较小的答案。

  4. 标记已分割区域:将分割的区域标记为true,表示已经分割了正方形。

  5. 递归搜索:分割完成后,递归地对剩余未分割区域进行同样的操作,直到所有区域都被分割或无法再分割为止。

剪枝策略

  1. 记录最小答案:在搜索过程中,记录当前已经找到的最小分割正方形数量ans。如果当前统计的分割数量cnt大于等于ans,则停止进一步搜索。

  2. 从大到小枚举:在枚举分割正方形的边长时,从大到小枚举,这样能够尽早找到一个较小的答案,从而在后续搜索中减少搜索量。

  3. 边界检查:在尝试分割正方形时,检查是否超出土地边界,如果超出则不进行分割。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<queue>
#include<set>
#include<list>
#include<sstream>
#include<bitset>
#include<stack>
#include<climits>
using namespace std;
bool g[15][15];
int m, n;
int res = INT_MAX;
// 左上角顶点为x y 长度为len的正方形可否填充
bool check(int x, int y, int len) {
    for (int i = 0; i < len; ++i) {
        for (int j = 0; j < len; ++j) {
            if (g[x + i][y + j]) return false;
        }
    }
    return true;
}
// 取反 
void fill_g(int x, int y, int len) {
    for (int i = 0; i < len; ++i) {
        for (int j = 0; j < len; ++j) {
            g[x + i][y + j] =!g[x + i][y + j];
        }
    }
}
// 从 x y开始枚举 cnt当前填充的正方形数量
void dfs(int x, int y, int cnt) {
    if (cnt >= res) return;
    if (x == m) {
        res = min(res, cnt);
        return;
    }
    bool flag = true;
    //只枚举列
    for (int j = y; j < n; j++) {
        if (!g[x][j]) {
            flag = false;
            // 右边和下面空余的长度 数组大小为m n
            for (int k = min(m - x, n - j); k >= 1; k--) {
                if (check(x, j, k)) { //可以填充
                    fill_g(x, j, k);
                    dfs(x, j + k, cnt + 1);
                    fill_g(x, j, k);
                 }
            }
            break;
        }
    }
    // 行全部满了
    if (flag) dfs(x + 1, 0, cnt);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> m >> n;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            g[i][j] = false;
        }
    }
    dfs(0, 0, 0);
    cout << res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值