目录
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 的特点:它的搜索方式是一路走到底(可能撞了南墙才会回头吧)
- 为了求得问题的解,选择某一种可能情况向前探索;
- 在探索过程中,一旦发现原来的选择是错误的,就退回一步重新选择,继续向前探索;
- 如此反复进行,直至得到解或证明无解。
思想:dfs使用的数据结构是stack 栈(一般直接用系统栈)dfs中最重要的算法思想是回溯和剪枝。另外dfs不具有最短性。一般是通过时间换空间
回溯是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
剪枝,就是减小搜索树规模、尽早排除搜索树中不必要的分支的一种手段。