给定一个数组,将数组内数字重新排序,使得相邻的两个数字之和为完全平方数,求有多少种排列方法。如果两种方法每一位上的数字都相同,则视为一种。本题我的思路就是首先把每个数字看作节点,如果两个数字的和是完全平方数就可以连一条边,然后就可以开始回溯。在回溯的过程中,在当前状态下,如果选择的数字之前选择过,则不需要再选择,具体见代码。(第一次在力扣上写题解)
class Solution {
public:
int vis[15],ans;//vis[i]表示第i个数字是否已经使用过
int equ[13][13];//equ[i][j]=1表示第i个数和第j个数相等
vector<int> G[13];//记录图中各节点的连接
//now表示当前选择第now个节点,x表示当前节点是原数组的第x个数,end是数组大小
void dfs(int now,int x,int end)
{
//记录当前选择的数字在之前是否使用过,如果使用过就可视为相同的情况,直接跳过。
int trys[12]={0};
//当前的数组顺序符合条件,答案加一
if(now==end-1)
{
ans++;
return ;
}
//每次选择一个节点去进行回溯,第一个节点的选择没有限制
if(now==-1){
for(int i=0;i<end;i++)
{
int ok=1;
//查询之前是否使用过相同的数字,有就跳过
for(int j=0;j<i;j++)
if(equ[i][j]&&trys[j])
{
ok=0;break;
}
if(ok)
{
trys[i]=1;
vis[i]=1;
dfs(now+1,i,end);
vis[i]=0;
}
}
}
//选择第now+1个节点,只能选择与now节点能组成完全平方数的数字
else{
for(int i=0;i<G[x].size();i++)
if(!vis[G[x][i]])
{
int ok=1;
for(int j=0;j<i;j++)
if(equ[G[x][i]][G[x][j]]&&trys[G[x][j]])
{
ok=0;break;
}
if(ok)
{
trys[G[x][i]]=1;
vis[G[x][i]]=1;
dfs(now+1,G[x][i],end);
vis[G[x][i]]=0;
}
}
}
}
int numSquarefulPerms(vector<int>& A) {
int sz=A.size();
int sum,t;
for(int i=0;i<sz;i++)
for(int j=i+1;j<sz;j++)
{
if(A[i]==A[j]) equ[i][j]=equ[j][i]=1;
sum=A[i]+A[j];
t=sqrt(sum);
//如果两个数字加起来是完全平方数,就连一条线
if(t*t==sum)
{
G[i].push_back(j);
G[j].push_back(i);
}
}
ans=0;
dfs(-1,-1,sz);
return ans;
}
};