正方形数组的数目(DFS,剪枝)
题意
给定一个包含\(N\)个非负整数数组\(A\),如果该数组每对相邻元素之和是一个完全平方数,则称这一数组为正方形数组。
返回\(A\)的正方形排列的数目。
两个排列\(A1\)和\(A2\)不同的充要条件是存在某个索引\(i\),使得\(A1[i] \neq A2[i]\)。
题目链接:https://www.acwing.com/problem/content/4522/
数据范围
\(1 \leq N \leq 12\)
思路
假设所有的数字都是不同的,这种情况下方案数并不多,直接爆搜即可。
但是如果有一些数是相同的,那么方案数就会多很多。正常的爆搜是无法通过的,因此考虑如何对这种情况剪枝。
我们能够观察到一个性质,当某个位置枚举到某个数的时候,如果与这个数相同数值(下标小)的数之前被枚举过,那么再往后枚举就没有意义了,因为全都已经枚举过了。
例如:[1 3 6 6 6 10]。爆搜出前2个数是[1 3],那么第3个数是哪个6都一样,因此枚举完下标是3的那个6之后,后面两个6可以直接跳过了。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 20;
int n;
int a[N], b[N];
int ans;
bool st[N];
void dfs(int u)
{
if(u == n + 1) {
ans ++;
return;
}
for(int i = 1; i <= n; i ++) {
if(st[i]) continue;
int t = sqrt(a[i] + b[u - 1]);
if(u > 1 && a[i] + b[u - 1] != t * t) continue;
if(i > 1 && a[i] == a[i - 1] && !st[i - 1]) continue;
st[i] = true;
b[u] = a[i];
dfs(u + 1);
st[i] = false;
}
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
dfs(1);
printf("%d\n", ans);
return 0;
}