一、题目回顾

题目链接:Square

题意:给你M根木棒,请判断这些木棒能否组成正方形。

二、解题思路
  • DFS+剪枝

【变量说明】

sum:木棒的总长度

ave:形成的正方形的边长

maxlen:最长木棒的长度

【剪枝】

  1. “num%4!=0”:表示木棒的总长度不是4的倍数,那么不能形成正方形
  2. “maxlen>ave”:表示最长木棒的长度大于“边长”,也不可能形成正方形
  3. 还有dfs语句内的剪枝,见下面分析。

【搜索过程】

dfs代码:

void dfs(int num,int len,int start)        //已凑完的边数,正在凑的这条边的长度,从序号为start的木棍开始找
{
//语句块1
if(num==){
flag = ;
return; //有4条边时,即可返回
}
//语句块2
if(len==ave){
dfs(num+,,);
}
//语句块3
for(int i=start;i<m;i++){ //遍历自start开始的边
if(!vis[i] && a[i]+len<=ave){ //如果这条边没访问过且加上这条边的长度小于边长
vis[i] = ; //将这条边标记
dfs(num,a[i]+len,i+); //如此调用,则当len=ave时会调用dfs(num+1,0,0)           
if(flag) return; //如果略去此句,会超时
vis[i] = ; //此棒不可用
}
}
}

dfs

下面来具体解释一下dfs函数是怎么运作的。

【形式参数】

  • num:已经凑完的边数
  • len:正在凑的这条边的长度
  • start:从第start根木棍开始找满足条件的木棍

【语句块1】

  • “num==4”成立,即凑完4条边了,则令标记变量flag为1,并退出此轮调用;
  • 何为“退出此轮调用”?简单来说,这个返回是返回到调用它的那个语句块内,而不是main函数中。

【语句块2】

  • 当上级调用传入的实参与正方形的边长相等时,那么凑成的边数就加1,并调用dfs(num+1,0,0);
  • 能进入这个语句块,间接说明了凑完的边数小于4,那么调用dfs(num+1,0,0)就成立;
  • “dfs(num+1,0,0)”,即正在凑成的边为0,从序号为0的棍子开始查找(这样对其他已用于形成边的棍子无影响,因为用于凑成其他的边数的棍子已经被标记)。

【语句块3】

  • 能到达这一步,说明当前调用并不能满足“num==4”以及“len==sum/4”;
  • “for(int i=start;i<m;i++)”,即从序号为start的棍子开始寻找,直到最后一根棍子;
  • “if(!vis[i] && a[i]+len<=ave)”,即找到序号为i的棍子没被用于形成其他边并且它的长度+已经凑成长度<=边长;
  • “vis[i]=1”,找到这个可行的棍子后,就说明这个棍子被用于形成边了(打上标记);
  • “dfs(num,len+a[i],i+1)”:首先谈“i+1”,你可能会问,为何是i+1而不是start或者是0呢?其实,这相当于剪枝,由本语句块内的if语句的判定条件式能确定第start根木棍到第i根木棍之前没有木棍能用于形成这条边,故无需再次判定(即令其为i+1);
  • “dfs(num,len+a[i],i+1)”:再谈len+a[i],这个将正在凑的这条边的长度进行更新。由本语句块内的if语句的判定条件式能确定a[i]+len<=ave,试想,如果len+a[i]==ave,那么就会进入语句块2,然后调用dfs(num+1,0,0),此时凑完的边数加1,即离成功越来越近,再往后或者进入语句块1(num==4),或者进入语句块3;如果len+a[i]<ave,会再次进入语句块3,那么如果自第i+1根棍子开始至第m-1根棍子没有满足语句块3内if语句的判定条件的棍子,那么会结束此轮调用(这轮调用的发起者为第i根棍子),即表明第i根棍子不能加入这条正在形成的边内;
  • “if(flag)  return;”,如果上面的dfs语句能够进入语句块1而使flag=1,就会返回主函数(节省时间),否则不返回主函数;
  • “vis[i]=0”,这条棍子不能使这条边最终形成,故不选用此棍子,此棍子恢复自由。

【重点再理解】

  • vis[i] = 1;  dfs(num,a[i]+len,i+1);  if(flag)    return;  vis[i] = 0;

陈述法:第i根木棒满足未被选用且满足已凑成的长度+a[i]<=边长,那么我们就假设选用它(vis[i]=1),但实际上我们并不能确定它就一定是这条边的一部分,我们要检测在它之后加入的其他木棒能否形成正方形,于是我们dfs(num,a[i]+len,i+1)。这是一个调用函数,它的返回调用在语句块1中,显然,如果从语句块1中结束调用,会有flag=1,执行if(flag)语句,会成功return回到主函数,即整个DFS调用结束(因为num==4,正方形已形成),如果缺少if(flag)语句,会执行vis[i]=0,这会浪费大量时间。再回到dfs(num,a[i]+len,i+1),如果选用的第i根木棒不能使后面加入的木棒形成正方形(即恒进不了语句块1),那么就有flag=0,于是不会返回到主函数,会执行vis[i]=0,然后继续执行for语句,寻找其他的木棒。

【深搜的理解】

由于过程中要确定某根木棒是否确定成功被接收,它就得提前预知加入这根木棒后其它的木棒能不能匹配成功,就叫要求在遍历某个木棒时,对其后的木棒进行递归搜索(深搜的特点)。若能匹配成功,则标记当前小木棒为用过,可以直接返回(试探成功);若不能匹配,说明此棒目前不可用,将标记取消,待下一次搜索用。

三、代码

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,sum,maxlen,flag;
int a[];
bool vis[]; void dfs(int num,int len,int x) //已形成的边数,正在形成的边的长度,第几根木棒
{
if(num==){
flag = ;
return;
}
if(len==sum/){
dfs(num+,,);
}
for(int i=x;i<=m;i++){
if(!vis[i] && a[i]+len<=sum/){
vis[i] = ;
dfs(num,a[i]+len,i+); //剪枝:前面的棒棒已经访问过,他们并不能用于形成这条边
if(flag==) return; //省略会TLE
vis[i] = ;
}
}
} int main()
{
scanf("%d",&n);
while(n--){
scanf("%d",&m);
sum = ;maxlen = ;
for(int i=;i<=m;i++){
scanf("%d",&a[i]);
sum += a[i];
if(a[i]>maxlen) maxlen = a[i];
}
if(sum% || maxlen>sum/){ //剪枝:根据题意判断
printf("no\n");
continue;
}
flag = ;
memset(vis,,sizeof(vis));
dfs(,,);
if(flag==) printf("yes\n");
else printf("no\n");
}
return ;
}

DFS(1)——hdu1518Square的更多相关文章

  1. DFS(深度优先)算法编程实践

    DFS定义 DFS(Depth-First-Search)深度优先搜索算法,是搜索算法的一种.是一种在开发爬虫早期使用较多的方法.它的目的是要达到被搜索结构的叶结点 . 特点 每次深度优先搜索的结果必 ...

  2. 拓扑排序+DFS(POJ1270)

    [日后练手](非解题) 拓扑排序+DFS(POJ1270) #include<stdio.h> #include<iostream> #include<cstdio> ...

  3. DFS(一):深度优先搜索的基本思想

    采用搜索算法解决问题时,需要构造一个表明状态特征和不同状态之间关系的数据结构,这种数据结构称为结点.不同的问题需要用不同的数据结构描述. 根据搜索问题所给定的条件,从一个结点出发,可以生成一个或多个新 ...

  4. 深度优先搜索DFS(一)

      实例一  0/1背包问题:   有n件物品,每件物品的重量为w[i],价值为c[i].现在需要选出若干件物品放入一个容量为V的背包中,使得在选入背包的物品重量和不超过容量V的前提下,让背包中的物品 ...

  5. 万能的搜索--之DFS(二)

    (一)深度优先搜索(DFS) 我们先给出深度优先的解决办法,所谓深度优先搜索,在迷宫问题里就是不撞南墙不回头,能走得深一点就尽量深一点.如果碰到了墙壁就返回前一个位置尝试其他的方向.在<啊哈!算 ...

  6. DFS(二):骑士游历问题

    在国际象棋的棋盘(8行×8列)上放置一个马,按照“马走日字”的规则,马要遍历棋盘,即到达棋盘上的每一格,并且每格只到达一次.例如,下图给出了骑士从坐标(1,5)出发,游历棋盘的一种可能情况. [例1] ...

  7. DFS(四):剪枝策略

    顾名思义,剪枝就是通过一些判断,剪掉搜索树上不必要的子树.在采用DFS算法搜索时,有时候我们会发现某个结点对应的子树的状态都不是我们要的结果,这时候我们没必要对这个分支进行搜索,砍掉这个子树,就是剪枝 ...

  8. DFS(三):八皇后问题

    [例1]八皇后问题. 在一个8×8国际象棋盘上,放置8个皇后,每个皇后占一格,要求皇后间不会出现相互“攻击”的现象,即不能有两个皇后处在同一行.同一列或同一对角线上.问共有多少种不同的放置方法? (1 ...

  9. 【递归入门】组合+判断素数:dfs(递归)

    题目描述 已知 n 个整数b1,b2,…,bn,以及一个整数 k(k<n).从 n 个整数中任选 k 个整数相加,可分别得到一系列的和. 例如当 n=4,k=3,4 个整数分别为 3,7,12, ...

随机推荐

  1. 菜鸟笔记 -- Chapter 6.2.6 内部类

    6.2.6  内部类 在权限修饰符中,我们已经见过内部类了,但我们看到的只是冰山一角,这节我们详细介绍一下内部类,内部类可以分为成员内部类,局部内部类,匿名内部类,静态内部类.下面我们来讲解一下,在讲 ...

  2. Python常用模块之os和sys

    1.OS常用方法 os.access(path, mode) # 检验权限模式 os.getcwd() #获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirn ...

  3. 纯css实现移动端横向滑动列表

    前几天在公司做开发的时候碰到一个列表横向滑动的功能,当时用了iscroll做,结果导致手指触到列表的范围内竖向滑动屏幕滑动不了的问题. 这个问题不知道iscroll本身能不能解决,当时选择了换一种方式 ...

  4. Ajax提交乱码

    1. 页面码 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&g ...

  5. #leetcode刷题之路4-寻找两个有序数组的中位数

    给定两个大小为 m 和 n 的有序数组 nums1 和 nums2.请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)).你可以假设 nums1 和 nums2 不会 ...

  6. window系统下pycharm的破解配置

    将C:\Windows\System32\drivers\etc\hosts内的写出: linux系统的话在 /etc/hosts,必须修改权限:sudo chmod a=rwx hosts 0.0. ...

  7. collections模块的使用

    1. Counter counter是collections中的一个模块, 它能够统计出字符串/文本中的每一个元素出现的次数, 并可以对结果进行进一步的处理. 使用方法 传入: 字符串 默认返回: C ...

  8. Nodejs 使用 SerialPort 调用串口

    工作经常使用串口读写数据,electron 想要替代原来的客户端,串口成了必须要突破的障碍. get -->  https://github.com/EmergingTechnologyAdvi ...

  9. linux文件操作篇 (一)文件属性与权限

    文件的属性和权限是linux中 目录 和 文件 的两个基本特性. #属性: . 所有者属性 . 访问权限属性   -rwxrwxr-x #第一个字符是文件类别 -表示 普通文件 d 表示目录 b 表示 ...

  10. 算法竞赛入门经典-1.5.4 Q&A

    这小节考察实践能力,要求在不要查书.不要网上找答案,自己用实验的方法解决以下五个问题: 做这五道题时,好几道都没思路,违反了规则到网上找了一圈,居然没找到答案,于是打算写这篇博客.不知是否有更好的实践 ...