题目的意思很清楚,给一些边的边长,判断能不能构成一个正方形。直接dfs过不了,要加一点剪枝才可以。
1.所有边长的和应该是4的倍数,否则不肯能构成正方形,这一点很容易想到。
2.必须按一定顺序搜索,不然会有很多重复,比如你从前面搜的时候,后面的有些不满足条件,那么搜后面时候就没必要再去搜前面的这些,这一点很重要,我是按边长递减的顺序搜的,这样就可以避免很多重复搜,还有搜到之后就不用回溯,这一点让我TLE了好几次,这时可以剪到200ms左右。
3.还有一点也很简单,就是这些边中最长的边要小于要拼的这个正方形的边长的,加上这一点可以剪到16ms了
4.要想达到0ms,我觉的还要加上一点,其实还有很多重复搜的,比如,从3搜无法拼成一条边,那么后面就没有必要从3开时搜了,直接跳过即可,这一点一直没很好的实现,还在改进。
PKU 2362 Square http://acm.pku.edu.cn/JudgeOnline/problem?id=2362
Source Code
Problem: 2362 User: zhouxc
Memory: 204K Time: 16MS
Language: C++ Result: Accepted
#include "iostream"
using namespace std;
int test,n,s[21];
int L,edge;
bool visited[21],flag;
int comp(const void *p1,const void *p2)
{
return *(int *)p1 <*(int *)p2;
}
void Dfs(int cunt,int r,int length)
{
// cout<<cunt<<" "<<r<<" "<<length<<endl;
visited[r]=true;
if(cunt==4)
flag=true;
else if(!length) //当边拼成边长为edge时,表示拼好了一条边
{
length=edge;
cunt++; //所拼成长度为edge的边数加一
for(int i=1;i<n;i++) //从未拼的边中选择下一条最长的边进行搜索
if(!visited[i])
Dfs(cunt,i,length-s[i]);
}
for(int i=r+1;i<n;i++) //按边长递增的顺序搜索
{
if(!visited[i]&&length-s[i]>=0)
Dfs(cunt,i,length-s[i]);
}
if(!flag) //当未拼成正方形时,回溯
{
visited[r]=false;
cunt--;
}
}
int main()
{
scanf("%d",&test);
while(test--)
{
int sum=0;int M=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&s[i]);
sum+=s[i];
visited[i]=false;
if(M<s[i])
M=s[i];
}
edge=sum/4;
if(sum%4==0&&M<=edge) //判断可否构成正方形
{
flag=false;
qsort(s,n,sizeof(s[0]),comp); //对边进行递减排序
Dfs(1,0,edge-s[0]); //从最长的边开始搜索
if(flag)
printf("yes/n");
else
printf("no/n");
}
else
printf("no/n");
}
return 0;
}