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
深搜过程类似DFS Network Saboteur poj 2531。
都是将棍子分为使用过和未使用过两部分。枚举所有情况
//208K 235MS
#include<iostream>
#include<stdlib.h>
#include<cstdio>
#include<cstring>
using namespace std;
#include<algorithm>
int average;
boolused[150];
int cases,num;
int a[150];
int sum;
bool cmp(int a,int b){//比较函数,从小到大 ,一维数组不用*a
return a>b;
}
int couside;
int dfs(int start,int sideNum,int cur_len){//功能:判断cur_len是否能构成一边,sideNum==3;如果sideNum!=3,从start开始枚举其后每根棍子的使用情况(使用和不使用)
//起始棍子编号,当前边的数量,当前边的长度
if(cur_len==average){//能构成一条边
sideNum++;
start=0;//从头继续搜索
cur_len=0;
}
if(sideNum==3) return 1;//dfs出口,剪枝,当能构成3条边时,必能构成第四条边
for(int i=start;i<num;i++){//从start开始枚举其后每根棍子的使用情况(使用和不使用)
if(used[i]||a[i]+cur_len>average) continue;//用过的或太长
used[i]=true; //标为用过
if(dfs(i+1,sideNum,cur_len+a[i]))//判断使用当前棍子i后cur_len+a[i]是否能构成一边,以及sideNum==3;如果sideNum!=3,从i+1开始枚举其后每根棍子的使用情况(使用和不使用)
return 1;//当最底层的sideNum==3时,逐层返回1,最终返回1
used[i]=false;//回溯,标记为未用过。
}
return 0;
}
int main(){
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
cin>>cases;
while(cases--){
int _max=0;
sum=0;
memset(used,false,sizeof(used));
average=0;
cin>>num;
for(int i=0;i<num;i++){
cin>>a[i];
average+=a[i];
_max=max(_max,a[i]);
}
sort(a,a+num,cmp);
if(average%4!=0||_max>average){//剪枝
cout<<"no"<<endl;
continue;
}
else average=average/4;
if(dfs(0,0,0)) cout<<"yes"<<endl;
else cout<<"no"<<endl;
}
return 0;
}