dfs:[NOIP2002]矩形覆盖(图解+思路+代码注解)详解

目录

题目:

 样例:​编辑

 思路:

代码注解:

dfs 的特点:它的搜索方式是一路走到底(可能撞了南墙才会回头吧)


题目链接:https://ac.nowcoder.com/acm/problem/16741

题目:


 样例:

 思路:

首先题目的意思是说最多给你四个矩形,让你求把这些点的包含进去且使得整个面积最小,所以

这题我们直接暴力深搜即可,也就是把每种情况都枚举出来,然后取得最小值;

然后, 因为n的点数很多,所以我们要学会优化也就是——剪枝

如何剪呢?我们不是要求最小吗?如果说我们当前已经求出一种方案的面积为S,那么假如下一种存取的方案过程中还没有把N个点存进去当前的面积就已经大于S了,那么就没必要在继续下去了直接return;结束这条道路的搜索

出口(结束的条件)在哪里?很显然,当我把n个点都分配完,也就是结束啦!要注意WOW,矩形之间不能有相交的地方(题目要求的),所以在我们分出这种情况的时候也判断一下每个矩形是否有相交,=》只需要抓住每个矩形的四个顶点去判断就可以了


假如N=4,且分别为ABCD,k=2;分法:(这是所有的情况,所以还有进行判断是否分的每个矩形面积之间没有交集,然后选出面积最小的一个)


 

 

代码注解:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n, k;
int x[60], y[60];
int res = 1e9;
struct node{
    int lx, ly, rx, ry;
}mtr[5];
int add(int now, int last){         //加入点并返回增加面积
    if(mtr[last].lx == -1){         //如果当前矩形没有点,直接修改为当前加入点
        mtr[last].lx = mtr[last].rx = x[now];
        mtr[last].ly = mtr[last].ry = y[now];
        return 0;
    }
    int s1 = (mtr[last].rx - mtr[last].lx) * (mtr[last].ly - mtr[last].ry);  //否则扩展矩形到加入点
    if(mtr[last].lx > x[now]) mtr[last].lx = x[now];
    if(mtr[last].rx < x[now]) mtr[last].rx = x[now];
    if(mtr[last].ly < y[now]) mtr[last].ly = y[now];
    if(mtr[last].ry > y[now]) mtr[last].ry = y[now];//扩展矩形的面积
    int s2 = (mtr[last].rx - mtr[last].lx) * (mtr[last].ly - mtr[last].ry);
    return s2 - s1;                      //返回增加面积
}
bool rect(int id, int x, int y){         //判断点是否在矩形内
    if(mtr[id].ry <= y && y <= mtr[id].ly && mtr[id].lx <= x && x <= mtr[id].rx) return true;
    else return false;
}
bool check(){                           //判断是否有矩形相交
    for(int i = 1; i <= k; i++){
        for(int j = i + 1; j <= k; j++){//判断矩形的四个点是否在矩形里面
            if(rect(i, mtr[j].lx, mtr[j].ly)) return true;
            if(rect(i, mtr[j].lx, mtr[j].ry)) return true;
            if(rect(i, mtr[j].rx, mtr[j].ly)) return true;
            if(rect(i, mtr[j].rx, mtr[j].ry)) return true;
        }
    }
    return false;
}
void dfs(int now, int sum){         //搜索
    if(sum >= res) return;//剪枝,在分矩形的过程中如果分得后最后的面积大于之前的分法的面积,那么就没必要在继续分下去了
    if(now == n + 1){//出口当n个点都分完后,判断k个矩形快之间有没有相交的部分
        if(!check()) res = min(res, sum);
        return;//保证每个矩形块之间没有相交的部分,把res更新一下,如果不符合则忽略这一种分法
    }
    for(int i = 1; i <= k; i++){
        node tmp = mtr[i];
        dfs(now + 1, sum + add(now, i));
        mtr[i] = tmp;//恢复矩形块的面积,回溯
    }
}
int main(){
    cin >> n >> k;
    for(int i = 1; i <= n; i++) cin >> x[i] >> y[i];
    for(int i = 1; i <= k; i++) mtr[i].lx = mtr[i].ly = mtr[i].rx = mtr[i].ry = -1;  //初始化,-1代表矩形里面没有点
    dfs(1, 0);
    cout << res;
    return 0;
}

dfs 的特点:它的搜索方式是一路走到底(可能撞了南墙才会回头吧)

  1. 为了求得问题的解,选择某一种可能情况向前探索;
  2. 在探索过程中,一旦发现原来的选择是错误的,就退回一步重新选择,继续向前探索;
  3. 如此反复进行,直至得到解或证明无解。

思想:dfs使用的数据结构是stack 栈(一般直接用系统栈)dfs中最重要的算法思想是回溯和剪枝。另外dfs不具有最短性。一般是通过时间换空间

  回溯是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

剪枝,就是减小搜索树规模、尽早排除搜索树中不必要的分支的一种手段。
 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值