POJ-2362 - Square (DFS + 剪枝)

Square

题目链接:A - Square POJ - 2362

题意

给你n个小木棍,要求全部用上,能否组成一个正方形。


思路

我原先的方法,非常非常暴力,就开个dfs,来记录每个状态的值,反正每个木棍都是要组成一个边的,于是我就枚举它放边的过程,复杂度为4^20次方吧,果然会炸。

有些基本的优化就不谈了,比如最长的边大于平均边,边总和不是4的倍数。。。

现在考虑枚举时的优化。

我原先之所以会炸,跟状态重叠也有很大的关系,例如对于第一个木棍来说,显然放在四个边的状态都是一样的,第二个木棍也会有很多重复的,这里的边又特别大,不能用记忆化搜索,

所以现在我们就一条边一条边的枚举,显然在枚举到第三条边完成时可以直接返回OK,然后对于每条边如果放过了,那么现在就不用考虑,用vis来储存即可。

当然这些还不够,当前点和之前的点已经被扫描过了,所以,可以直接从下一个点开始扫。

这样复杂度就大大下降了。


代码

#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cstdio>
#include <string>
#include <stack>
#include <cmath>
#include <ctime>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <sstream>
#include <memory>
#include <iostream>
#include <algorithm>
using namespace std;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define per(i,j,k) for(int i = (int)j;i >= (int)k;i --)
#define debug(x) cerr<<#x<<" = "<<(x)<<endl
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back

typedef double db;
typedef long long ll;
const int MAXN = (int)20+7;
const int INF = (int)0x3f3f3f3f;

int A[MAXN];
int vis[MAXN];
int N;
int edge;
int flag;

void dfs(int num,int len,int s) {
    if (num == 3) {
        flag = 1;
        return ;
    }   
    per(i,s,1) {
        if (vis[i]) continue;
        vis[i] = 1;
        if (len + A[i] == edge) dfs(num+1,0,N);      if (flag) return;
        if (len + A[i] < edge)  dfs(num,len+A[i],i); if (flag) return;
        vis[i] = 0;
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T --) {
        scanf("%d",&N);
        int sum = 0;
        rep(i,1,N) {
            scanf("%d",&A[i]);
            sum += A[i];
        }
        sort(A+1,A+1+N);
        edge = sum/4;
        if (sum%4 != 0 || A[N] > edge) puts("no");
        else {
            flag = 0;
            mmm(vis,0);
            dfs (0,0,N);
            if (flag) puts("yes");
            else      puts("no");
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值