正方形数组的数目(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;
}
posted @ 2022-07-18 09:25  pbc的成长之路  阅读(30)  评论(0编辑  收藏  举报