題目大意
給你M根長度可能不同的棍子,問你用着M根棍子能否拼成一個正方形.
思路分析
首先將M根棍子的總長sum求出,sum%4必須==0且任意一個棍子的長度<=sum/4.
用vis[i]數組表示當前第i根棍子是否被使用了.
然後用dfs來判斷能否構成正方形的四邊即可.其中
bool dfs(int num, int len, int j)
表示當前正在構造第num
根棍子且當前棍子已經湊到了len
的長度,下一次從第j
個木棍開始搜索。這裏注意網上很多解法都是對棍子排序了的,其實不用排序也行.因爲我們用到的是定序剪枝,只要棍子的順序固定就行.
源代碼中最重要的剪枝是dfs函數中的下一次開始begin位置的記錄,假設我們當前這步與下一步都是在構造同一條邊,那麼我們下次搜索所有棍子只需要從begin+1位置開始找即可,不用從0開始選。爲什麼?
假設我們當前剛設置完vis[5]=1
,表示第5
根棍子我們在還是left
長度的時候用了,現在我們還是left-len[5]
長度的時候要不要去用len[2]
來嘗試配對一下?不用,肯定無解.因爲搜索的時候我們是先找的len[2]
的,如果len[2]+len[5]+….
有一個可行解(可以配對成當前cnt
邊)的話,我們之前肯定已經找到了且推出了dfs這個函數.但是現在明顯我們還沒找到,所以len[2]+len[5]+…
不可能是一個可行解,所以直接從5後面的位置繼續找即可.如果還不能理解,就把本題想象成我們只需要配對一條正方形的邊,不用配4邊了.
//164K 79MS
#define inf 0x3f3f3f3f
#define vec vector<int>
#define P pair<int,int>
#define ll long long
#define MAX 25
int T, n, a[MAX], vis[MAX], sum, ma;
//目前在拼湊num個木棍,第num個長度爲0,上一個用了j號木棍
bool dfs(int num, int len, int j) {
if (num == 5)return true;
for (int i = j + 1; i < n; i++) {
if (vis[i])continue;
if (len + a[i] == sum / 4) {
vis[i] = 1;
if (dfs(num + 1, 0, 0))return true;
vis[i] = 0;
}
else if (len + a[i] < sum / 4) {
vis[i] = 1;
if (dfs(num, len + a[i], i))return true;
vis[i] = 0;
}
}
return false;
}
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
sum = 0; ma = 0;
for (int i = 0; i < n; i++)
scanf("%d", &a[i]), sum += a[i], ma = max(ma, a[i]);
if (ma > sum / 4 || sum % 4 != 0) {
cout << "no" << endl;
continue;
}
memset(vis, 0, sizeof(vis));
if (dfs(1, 0, -1))cout << "yes" << endl;
else cout << "no" << endl;
}
}