您的位置:首页 > 其它

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边了.

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dfs 剪枝