UVA10364 题解

发布时间 2024-01-03 16:14:06作者: ZnHF

题意简述

给定 \(n\) 根木棍,第 \(i\) 根的长度为 \(a_{i}\),求能否使用全部木棍拼成一个正方形。

题目分析

这道题和 P1120 很像,都考察了对于 DFS 的剪枝优化。

具体地,我们有以下几个剪枝策略

  1. 计算出每根木棍的长度之和,记为 \(sum\),若 \(sum \bmod 4 \neq 0\),则直接输出 no

  2. 将每一根木棍按长度从大到小排序,优先尝试将长度更长的木棍拼入正方形中。

  3. 若下一层搜索返回失败,则不再尝试将长度和上一次拼入正方形的木棍的长度一样的木棍拼入正方形。

  4. 若下一层搜索返回失败,且当前拼出的边的长度为 \(0\) 则直接返回失败。原因是其他没有拼过的边和当前在考虑的空的边是等效的,将某跟木棍拼入当前的边若失败,则将该木棍拼入其他空的边同样会失败。

  5. 若将当前考虑的某根木棍拼入当前考虑的边后,边长达到了要求,但后续的搜索返回失败,则直接返回失败。原因是较短的木棍比较长的木棍更优,当为后续的搜索留下更多更短的木棍都无法拼成时,若在当前使用更短的木棍,则后续的搜索一定无法成功。

讲得可能不太好,所以具体实现看代码。

Code

#include<bits/stdc++.h>
using namespace std;
int T,n,a[25],sum;
bool vis[25];
bool cmp(int x,int y){
	return x>y;
}
bool dfs(int finish,int now_len,int last){
	if(finish>=4) return 1;
	if(now_len==sum) return dfs(finish+1,0,1);
	int never=0;
	for(int i=last;i<=n;i++){
		if(!vis[i] && now_len+a[i]<=sum && never!=a[i]){
			vis[i]=1;
			if(dfs(finish,now_len+a[i],i+1)) return 1;
			never=a[i];
			vis[i]=0;
			if(!now_len || now_len+a[i]==sum) return 0;
		}
	}
	return 0;
}
int main(){
	cin>>T;
	while(T--){
		cin>>n;
		sum=0;
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=n;i++){
			cin>>a[i];
			sum+=a[i];
		}
		if(sum%4!=0){
			puts("no");
			continue;
		}
		sort(a+1,a+1+n,cmp);
		sum/=4;
		if(dfs(0,0,1)==1) puts("yes");
		else puts("no");
	}
	return 0;
}