POJ2362-Square

全解题报告索引目录 -> 【北大ACM – POJ试题分类

转载请注明出处:http://exp-blog.com

-------------------------------------------------------------------------

 

大致题意:

给定一堆不定长度的小棒子,问他们能否构成一个正方形。

 

解题思路:

POJ1011的热身题,DFS+剪枝

 

本题大致做法就是对所有小棒子长度求和sum,sum就是正方形的周长,sum/4就是边长side。

问题就转变为:这堆小棒子能否刚好组合成为4根长度均为side的大棒子

 

不难了解,小棒子的长度越长,其灵活性越差。例如长度为5的一根棒子的组合方式要比5根长度为1的棒子的组合方式少,这就是灵活性的体现。

由此,我们首先要对这堆小棒子降序排序,从最长的棒子开始进行DFS

 

剪枝,有3处可剪:

1、  要组合为正方形,必须满足sum%4==0;

2、  所有小棒子中最长的一根,必须满足Max_length <= side,这是因为小棒子不能折断;

3、  当满足条件1、2时,只需要能组合3条边,就能确定这堆棒子可以组合为正方形。

 

//Memory Time 
//244K   79MS 

#include<iostream>
#include<algorithm>
using namespace std;

int cmp(const void* a,const void* b)  //降序
{
	return *(int*)b-*(int*)a;
}

int n;  //木棒数量
int side;  //正方形边长
bool dfs(int* stick,bool* vist,int num,int len,int s)  //num:已组合的正方形的边数  len:当前组合的边已组合的长度,len<=side
{                                                      //s:stick[]的搜索起点
	if(num==3)   //剪枝3,当满足剪枝1和2的要求时,只需组合3条side,剩下的棒子必然能够组成最后一条side
		return true;

	for(int i=s;i<n;i++)
	{
		if(vist[i])
			continue;

		vist[i]=true;
		if(len+stick[i]<side)
		{
			if(dfs(stick,vist,num,len+stick[i],i))  //继续构建当前side
				return true;
		}
		else if(len+stick[i]==side)
		{
			if(dfs(stick,vist,num+1,0,0))  //构建新side
				return true;
		}
		vist[i]=false;
	}

	return false;
}

int main(void)
{
	int time;
	cin>>time;
	for(int t=0;t<time;t++)
	{	
		int sum=0;  //所有木棒长度之和

		cin>>n;
		int* stick=new int[n];
		bool* vist=new bool[n];

		for(int i=0;i<n;i++)
		{
			cin>>stick[i];
			vist[i]=false;

			sum+=stick[i];
		}		

		if(n<4 || sum%4!=0)    //剪枝1,不能构成正方形
		{
			cout<<"no"<<endl;
			continue;
		}

		qsort(stick,n,sizeof(int),cmp);   //降序排列,先组合长棒,因为长棒相对于短棒的组合灵活性较低

		side=sum/4;  //边长
		if(side<stick[0])   //剪枝2,side<max_stick
		{
			cout<<"no"<<endl;
			continue;
		}

		if(dfs(stick,vist,0,0,0))
			cout<<"yes"<<endl;
		else
			cout<<"no"<<endl;

		delete stick;
		delete vist;
	}
	return 0;
}


 

 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值