poj2362Square

Square
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 21988 Accepted: 7691

Description

Given a set of sticks of various lengths, is it possible to join them end-to-end to form a square?

Input

The first line of input contains N, the number of test cases. Each test case begins with an integer 4 <= M <= 20, the number of sticks. M integers follow; each gives the length of a stick - an integer between 1 and 10,000.

Output

For each case, output a line containing "yes" if is is possible to form a square; otherwise output "no".

Sample Input

3
4 1 1 1 1
5 10 20 30 40 50
8 1 7 2 6 4 4 3 5

Sample Output

yes
no
yes

Source

Waterloo local 2002.09.21

很不错的一道题,就是说 给若干个小木棍,看是否能拼成一个正方形形,拼成一个矩形要满足所有木棍的总长度对4取余等于0,还得满足最长的一根小木棍不能长于所拼成正方形的边长。

正常考虑,要所围成的图形为正方形,需要检测一条边和别的边长度加到一块是否为正方形的边长,这个边长可能为一个木棍,也可能是两个木棍,三个木棍,四个,五个.....

计算时 计算到当前所加的木棍长度,如果当前的长度加上下一个木棍的长度大于所拼成正方形的边长,则不能判定为次状态无解,可能是加上别的一条,两条,三条...等于了所求的边长,如果把所有的情况加上了依然无解,依旧不能断定次木棍不能组成矩形,还可能当前本身加上的木棍长度就不合理,所以本题不用深搜无解

深搜(深入优先搜索(DFS)),当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索,算法复杂度很高,就是把所有的点都遍历了一遍,不过代码很简单,很容易写,毕竟是时间复杂度为(O(n!));

本题大致做法就是对所有小棒子长度求和sum,sum就是正方形的周长,sum/4就是边长side。
问题就转变为:这堆小棒子能否刚好组合成为4根长度均为side的大棒子
 
不难了解,小棒子的长度越长,其灵活性越差。例如长度为5的一根棒子的组合方式要比5根长度为1的棒子的组合方式少,这就是灵活性的体现。
由此,我们首先要对这堆小棒子降序排序,从最长的棒子开始进行DFS
 
剪枝,有3处可剪:
1、  要组合为正方形,必须满足sum%4==0;
2、  所有小棒子中最长的一根,必须满足Max_length <= side,这是因为小棒子不能折断;
3、  当满足条件1、2时,只需要能组合3条边,就能确定这堆棒子可以组合为正方形。
 

附ac代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[21],i,j,k,l,m,n,mid,vis[1000],num;
long sum;
int dfs(int num,int len,int s) 
{                                                      
    if(num==3)  //如果已经得到的可构成变长的数位3  则不需要计算,剩下的一定能构成  
        return 1;  
    for(int i=s;i<=n;i++)  
    {
        if(vis[i])  //如果该木棍已经被使用,则无法继续使用 
            continue;  
  
        vis[i]=1;  //如果该木棍未被使用,则把该木棍标记为已经使用过了 
        if(len+a[i]<mid)  //得到的当前长度未达到边长 
        {  
            if(dfs(num,len+a[i],i))  //继续访问下一个 
                return 1;  
        }
        else if(len+a[i]==mid)  //得到的当前长度达到边长
        {  
            if(dfs(num+1,0,1))  //num+1,当前长度记为0; 
                return 1;  
        }  
        vis[i]=0;  //如果未到达要求,释放该点,以备下次别的情况访问 
    }  
    return 0;  
}  
int main()
{
	scanf("%d",&m);
	while(m--)
	{
		scanf("%d",&n);
		sum=0;
		for(i=1;i<=n;i++)
		{
		scanf("%d",&a[i]);
		sum+=a[i];//先求和,得到总长度。 
		}
		sort(a+1,a+n+1);//将所有的 木棍进行从小到大排序,sort里边逗号是该数组最后一个成员的地址的下一位 
		mid=sum/4;//在进行除法,得到边长 
		if(mid*4!=sum)//如果边长非整数 ,则不需要计算 
		//if(sum%4) 
		printf("no\n");
		else if(a[n]>mid)//如果最长的边大于了边长,因为小木棍不可分割,所以也不需要判断 
		printf("no\n");
		else
		{
		num=0;
		memset(vis,0,sizeof(vis));
		if(dfs(0,0,1))//第一个0代表 已经得到的可构成的边长数,第二个代表当前所得木棍和的长度,1代表搜索的地址 
		printf("yes\n");
		else
		printf("no\n");
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值