POJ 2362/HDU 1518 dfs 剪枝
2017-08-23 00:00
323 查看
题意:
给你M根长度可能不同的棍子,问你用着M根棍子能否拼成一个正方形.
分析:
首先将M根棍子的总长sum求出,sum%4必须==0且任意一个棍子的长度<=sum/4.
用vis[i]数组表示当前第i根棍子是否被使用了.
然后用dfs来判断能否构成正方形的三边即可.其中dfs(int cnt,int left)表示当前正在构造第cnt根棍子且当前棍子还剩下left长度需要构造.这里注意网上很多解法都是对棍子排序了的,其实不用排序也行.因为我们用到的是定序剪枝,只要棍子的顺序固定就行.
源代码中最重要的剪枝是dfs函数中的begin位置的记录,假设我们当前这步与下一步都是在构造同一条边,那么我们下次搜索所有棍子只需要从begin+1位置开始找即可,不用从0开始选。为什么?
假设我们当前刚设置完vis[5]=1,表示第5根棍子我们在还是left长度的时候用了,现在我们还是left-len[5]长度的时候要不要去用len[2]来尝试配对一下?不用,肯定无解.因为搜索的时候我们是先找的len[2]的,如果len[2]+len[5]+….有一个可行解(可以配对成当前cnt边)的话,我们之前肯定已经找到了且推出了dfs这个函数.但是现在明显我们还没找到,所以len[2]+len[5]+…不可能是一个可行解,所以直接从5后面的位置继续找即可.如果还不能理解,就把本题想象成我们只需要配对一条正方形的边,不用配4边了.
【思路】
深搜:用这类型组合题目最基本的深搜,变量side记录当成已经组成了几条变,sl表示当前在组合的边已经有的长度。如果当前stick的长度与已有长度的和恰巧等于边长,则side+1,将sl清零;否则若小于边长,则sl+当前长度继续搜索,直到组成所有边为止。
剪枝:(1)如果木棒数目没有到达四根,则为no
(2)比较容易想到的一点,如果当前木棒总长不是4的整数倍,则为no
(3)由于木棒不能这段,如果最长的木棒大于边长,则为no
(4)由于越短的木棒灵活性越高,进行快排之后由大至小进行搜索。
(5)为了避免重复搜索,我们默认组成同一边时,下一根取的木棒长度必定不大于当前根的木棒。故设置变量frm,即当前可取木棒长度的范围。
剪枝
第一根无法拼成,则这个无法和其他的组合成功优化900ms-->60
给你M根长度可能不同的棍子,问你用着M根棍子能否拼成一个正方形.
分析:
首先将M根棍子的总长sum求出,sum%4必须==0且任意一个棍子的长度<=sum/4.
用vis[i]数组表示当前第i根棍子是否被使用了.
然后用dfs来判断能否构成正方形的三边即可.其中dfs(int cnt,int left)表示当前正在构造第cnt根棍子且当前棍子还剩下left长度需要构造.这里注意网上很多解法都是对棍子排序了的,其实不用排序也行.因为我们用到的是定序剪枝,只要棍子的顺序固定就行.
源代码中最重要的剪枝是dfs函数中的begin位置的记录,假设我们当前这步与下一步都是在构造同一条边,那么我们下次搜索所有棍子只需要从begin+1位置开始找即可,不用从0开始选。为什么?
假设我们当前刚设置完vis[5]=1,表示第5根棍子我们在还是left长度的时候用了,现在我们还是left-len[5]长度的时候要不要去用len[2]来尝试配对一下?不用,肯定无解.因为搜索的时候我们是先找的len[2]的,如果len[2]+len[5]+….有一个可行解(可以配对成当前cnt边)的话,我们之前肯定已经找到了且推出了dfs这个函数.但是现在明显我们还没找到,所以len[2]+len[5]+…不可能是一个可行解,所以直接从5后面的位置继续找即可.如果还不能理解,就把本题想象成我们只需要配对一条正方形的边,不用配4边了.
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 25; int M, sum;//sum表示棍子总长 int len[maxn]; int vis[maxn]; bool dfs(int cnt, int left, int begin){//当前匹配第cnt条边,且该边剩余left长度,且当前轮从begin开始选棍子 if (cnt == 4) return true; //构造成功 else for (int i = begin; i < M; i++) if (!vis[i]){ vis[i] = true; if (left == len[i]){ if (dfs(cnt + 1, sum / 4, 0)) return true; } else if (len[i] < left){ if (dfs(cnt, left - len[i], i + 1)) return true; } vis[i] = false; } return false; } int main(){ int T; scanf("%d", &T); while (T--){ int max_len = 0; sum = 0; scanf("%d", &M); for (int i = 0; i < M; i++){ scanf("%d", &len[i]); max_len = max(max_len, len[i]); sum += len[i]; } if (sum % 4 != 0 || max_len > sum / 4){ printf("no\n"); continue; } //sort(len,len+M); //可以不用排序 memset(vis, 0, sizeof(vis)); if (dfs(1, sum / 4, 0)) printf("yes\n"); else printf("no\n"); } return 0; }
【思路】
深搜:用这类型组合题目最基本的深搜,变量side记录当成已经组成了几条变,sl表示当前在组合的边已经有的长度。如果当前stick的长度与已有长度的和恰巧等于边长,则side+1,将sl清零;否则若小于边长,则sl+当前长度继续搜索,直到组成所有边为止。
剪枝:(1)如果木棒数目没有到达四根,则为no
(2)比较容易想到的一点,如果当前木棒总长不是4的整数倍,则为no
(3)由于木棒不能这段,如果最长的木棒大于边长,则为no
(4)由于越短的木棒灵活性越高,进行快排之后由大至小进行搜索。
(5)为了避免重复搜索,我们默认组成同一边时,下一根取的木棒长度必定不大于当前根的木棒。故设置变量frm,即当前可取木棒长度的范围。
#include <iostream> #inc 7fe0 lude <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 20 + 10; int stick[MAXN]; int n, sum, m; bool visited[MAXN]; bool dfs(int frm, int side, int sl){ if (3 == side) return (true); for (int i = frm; i >= 0; i--) if (!visited[i]){ visited[i] = true; if (stick[i] + sl < sum){ if (dfs(i - 1, side, sl + stick[i])) return true; } else if (stick[i] + sl == sum){ if (dfs(m - 1, side + 1, 0)) return true; } visited[i] = false; } return false; } int main(){ scanf("%d", &n); for (int kase = 0; kase < n; kase++){ memset(visited, false, sizeof(visited)); scanf("%d", &m); sum = 0; int max = -1; for (int i = 0; i < m; i++){ scanf("%d", &stick[i]); sum += stick[i]; if (stick[i] > max) max = stick[i]; } if (sum % 4 != 0 || m < 4 || max > sum / 4) cout << "no" << endl; else { sort(stick, stick + m); sum = sum / 4; if (dfs(m - 1, 0, 0)) cout << "yes" << endl; else cout << "no" << endl; } } return 0; }
剪枝
第一根无法拼成,则这个无法和其他的组合成功优化900ms-->60
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #define LL long long int const MAX = 1e6 + 1; int const INF = 1 << 30; double const EPS = 0.00000001; using namespace std; int T, n, a[22], sum; bool vis[22]; //对第cnt条边,由cur开始匹配,剩下rest bool dfs(int cur, int rest, int cnt){ if (cnt == 3) return 1; //已经匹配了三根,则完成 for (int i = cur; i < n; i++){ if (vis[i] || rest < a[i]) continue; vis[i] = 1; if (a[i] == rest){//如果匹配成功后,由0开始重新查找 if (dfs(0, sum / 4, cnt + 1)) return 1; } else { if (dfs(i + 1, rest - a[i], cnt)) return 1; } vis[i] = 0; //如果第一个无法拼成,则剩下的都无法和这个组合成功 if (rest == sum / 4) return 0; //a[i] 失败,如果a[i+1]和a[i]相等,跳过 while (i + 1 < n && a[i + 1] == a[i]) i++; } return 0; } bool cmp(int x, int y){ return x > y; } int main(){ freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); scanf("%d", &T); while (T--){ scanf("%d", &n); sum = 0; memset(vis, 0, sizeof(vis)); for (int i = 0; i < n; i++) scanf("%d", a + i), sum += a[i]; sort(a, a + n, cmp); if (sum % 4 || a[0] > sum / 4){ printf("no\n"); continue; } printf("%s\n", dfs(0, sum / 4, 0) ? "yes" : "no"); } return 0; }
相关文章推荐
- POJ 2362 HDOJ 1518 Square DFS剪枝
- hdu 1518||poj 2362 Square(dfs)
- POJ 2362 && HDU 1518 Square(dfs)
- poj 2362 Square(dfs, 剪枝)
- poj 1564 Sum It Up | zoj 1711 | hdu 1548 (dfs + 剪枝 or 判重)
- POJ 2362 DFS+剪枝
- poj 1011/2362 dfs+剪枝(拼木棍)
- POJ 2362 Square (DFS +剪枝)
- hdu 1455/poj 1011 Sticks(DFS剪枝神题)
- POJ 2362 dfs 剪枝
- 2018.1.29【 HDU - 1518 】解题报告(dfs,极易TLE,剪枝)
- hdu 1518 Square【DFS+剪枝】
- poj 1564 Sum It Up | zoj 1711 | hdu 1548 (dfs + 剪枝 or 判重)
- 【DFS+剪枝】-HDU-1518-Squares
- HDU 1518 Square (DFS+剪枝)
- hdu 1518 DFS+剪枝
- 不要丧呀&&POJ 2362 && [剪枝]&&[dfs]
- POJ 1011/HDU 1455 dfs 剪枝
- poj 1011 sticks(木棒) (dfs+剪枝)
- hdu 1518 Square (dfs)