题目出处
思路:dfs+剪枝。
dfs:从头到尾把每种组合都搜一遍,如果遇到满足条件就疯狂return,但最后一个点TLE
剪枝:稍加思索就发现,每一行之和必须是所有数之和除以n,只要不满足这 个条件就return,这样就不会TLE了
代码简单易懂(最好不要在dfs里写上面的那个剪枝的判断,这样的话java很容易MLE):
package search;
import java.util.Arrays;
import java.util.Scanner;
public class P1406 {
static int n, arr[], res, temp[], tot;
static boolean vis[], flag;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
arr = new int[n * n + 1];
temp = new int[n * n + 1];
vis = new boolean[n * n + 1];
for (int i = 1; i <= n * n; i++) {
arr[i] = sc.nextInt();
}
for (int i = 1; i <= n * n; i++) {
tot = tot + arr[i];
}
Arrays.sort(arr, 1, n * n + 1);// 因为按照最小字典序,因此必须升序排列
dfs(1);
}
public static void dfs(int num) {
if (num > n * n && check(temp) && !flag) {
flag = true;
for (int i = 1; i <= n; i++) {
res = res + temp[i];
}
System.out.println(res);
for (int i = 1; i <= n; i++) {
for (int j = n * (i - 1) + 1; j <= n * i; j++) {
System.out.print(temp[j] + " ");
}
System.out.println();
}
return;
}
//比较重要的剪枝,没的话会TLE
if ((num - 1) % n == 0 && num >= n + 1) {// 只有当temp里的个数大于n+1且个数是n的倍数才进行判断这一行的和是不是tot/n
if (!sum(temp, (num - 1) / n)) {//判断第n行的和是不是tot/n
return;
}
}
if (flag) {
return;
}
if (num > n * n) {
return;
}
for (int i = 1; i <= n * n; i++) {
if (!vis[i]) {// 如果第i个数没被用过
vis[i] = true;
temp[num] = arr[i];
dfs(num + 1);
if (flag) {
break;
}
vis[i] = false;
}
}
}
/**
* 功能:判断第a行的和是不是tot/n,很容易证明幻方的每一行就是所有数的和除以n
* @param sz
* @param a
* @return
*/
public static boolean sum(int[] sz, int a) {
int s = 0;
for (int i = (a - 1) * n + 1; i <= a * n; i++) {
s += sz[i];
}
if (s == tot / n) {
return true;
}
return false;
}
/**
* 功能:判断是不是幻方(因为懒,所以直接手写了,也可以写一个适用于所有n的check函数)
* @param sz
* @return
*/
public static boolean check(int sz[]) {
if (n == 1) {
return true;
}
if (n == 2) {
int r[] = new int[7];
r[1] = sz[1] + sz[2];
r[2] = sz[3] + sz[4];
r[3] = sz[1] + sz[3];
r[4] = sz[2] + sz[4];
r[5] = sz[1] + sz[4];
r[6] = sz[2] + sz[3];
for (int i = 1; i <= 5; i++) {
if (r[i] != r[i + 1]) {
return false;
}
}
}
if (n == 3) {
int r[] = new int[9];
r[1] = sz[1] + sz[2] + sz[3];
r[2] = sz[4] + sz[5] + sz[6];
r[3] = sz[7] + sz[8] + sz[9];// 行
r[4] = sz[1] + sz[4] + sz[7];
r[5] = sz[2] + sz[5] + sz[8];
r[6] = sz[3] + sz[6] + sz[9];// 列
r[7] = sz[3] + sz[5] + sz[7];
r[8] = sz[1] + sz[5] + sz[9];// 对角线
for (int i = 1; i <= 7; i++) {
if (r[i] != r[i + 1]) {
return false;
}
}
}
if (n == 4) {
int r[] = new int[11];
r[1] = sz[1] + sz[2] + sz[3] + sz[4];
r[2] = sz[5] + sz[6] + sz[7] + sz[8];
r[3] = sz[9] + sz[10] + sz[11] + sz[12];
r[4] = sz[13] + sz[14] + sz[15] + sz[16];
r[5] = sz[1] + sz[5] + sz[9] + sz[13];
r[6] = sz[2] + sz[6] + sz[10] + sz[14];
r[7] = sz[3] + sz[7] + sz[11] + sz[15];
r[8] = sz[4] + sz[8] + sz[12] + sz[16];
r[9] = sz[1] + sz[6] + sz[11] + sz[16];
r[10] = sz[4] + sz[7] + sz[10] + sz[13];
for (int i = 1; i <= 9; i++) {
if (r[i] != r[i + 1]) {
return false;
}
}
}
return true;
}
}