题目点这里
思路:我选择一行一行搜,搜到每一行的最后一列就到下一行
原则:①能优化就优化 ②能剪枝就剪枝③少用循环~~呜呜呜~
注意:因为我们用的是Java,所以要务必小心随时TLE的风险~
1、玩过数独的都知道从数字多的那一行开始填
2、尽量别用for循环来求和,判断行列宫是否重复啥的
3、求和可以打表,虽然还是要for循环,但少了判断也是极好的
4、可以用布尔数组hang[i][shuzi]表示第i行是否有shuzi,列同理,宫我实在懒得想
5、如果你用for循环判断是否行列重复,极大可能会T(我就T了),用O(1)不香吗
6、代码简单易懂
package search;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
class pa {
int no;
int co;
public pa(int no, int co) {
this.no = no;
this.co = co;
}
}
public class P1074 {
static int map[][] = new int[9][9], max = -1, mxx, s, pai[] = new int[10],
score[][] = {
{ 6, 6, 6, 6, 6, 6, 6, 6, 6 },
{ 6, 7, 7, 7, 7, 7, 7, 7, 6 },
{ 6, 7, 8, 8, 8, 8, 8, 7, 6 },
{ 6, 7, 8, 9, 9, 9, 8, 7, 6 },
{ 6, 7, 8, 9, 10, 9, 8, 7, 6 },
{ 6, 7, 8, 9, 9, 9, 8, 7, 6 },
{ 6, 7, 8, 8, 8, 8, 8, 7, 6 },
{ 6, 7, 7, 7, 7, 7, 7, 7, 6 },
{ 6, 6, 6, 6, 6, 6, 6, 6, 6 } },
hang[][] = new int[9][9], lie[][] = new int[9][9];
static ArrayList<pa> list = new ArrayList<pa>();
public static void main(String[] args) throws IOException {
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
for (int i = 0; i < 9; i++) {
int xx = 0;
for (int j = 0; j < 9; j++) {
st.nextToken();
int temp = (int) st.nval;
if (temp != 0) {
hang[i][temp - 1] = 1;//代表第i行已经有数字temp
lie[j][temp - 1] = 1;//代表第i列已经有数字temp
}
map[i][j] = temp;
if (temp > 0) {
xx++;
}
}
list.add(new pa(i, xx));
}
Collections.sort(list, new Comparator<pa>() {//按每一行非零个数从大到小排序
@Override
public int compare(pa o1, pa o2) {
// TODO Auto-generated method stub
return o2.co - o1.co;// 降序排列
}
});
for (int i = 0; i < 9; i++) {//存数组 存数组
pai[i] = list.get(i).no;//pai[]大小为10是为了防止越界报错
}
list.clear();//清掉免得占空间,不清也可以没影响
dfs(pai[0], 0, 0, 0);
System.out.println(max);
}
// dfs
/**
*
* @param x 横坐标
* @param y 纵坐标
* @param num 填了几个数
*/
public static void dfs(int x, int y, int num, int index) {
if (num == 81) {// 填满了
max = Math.max(max, calc(map));//取最大
return;
}
if (x == -1) {//防止越界
dfs(pai[index + 1], 0, num, index + 1);
return;
}
// 一行一行的填
if (map[x][y] == 0) {// 如果这一点没有值
for (int i = 1; i <= 9; ++i) {// 开始填数
if (isRepeated(x, y, i)) {// 没重复
if (y + 1 < 9) {// 如果不是最后一列
map[x][y] = i;
hang[x][i-1] = 1;//现在第x行有了数字i
lie[y][i-1] = 1;//现在第y列有了数字i
dfs(x, y + 1, num + 1, index);// 接着填下一列
map[x][y] = 0;// 回溯
hang[x][i-1] = 0;//现在第x行没有了数字i
lie[y][i-1] = 0;//现在第y列没有了数字i
} else {// 如果是最后一列
map[x][y] = i;
hang[x][i-1] = 1;//现在第x行有了数字i
lie[y][i-1] = 1;//现在第y列有了数字i
dfs(pai[index + 1], 0, num + 1, index + 1);// 接着填下一行
map[x][y] = 0;// 回溯
hang[x][i-1] = 0;//现在第x行没有了数字i
lie[y][i-1] = 0;//现在第y列没有了数字i
}
}
}
} else {// 如果这一点有值
if (y + 1 < 9) {// 如果不是最后一列
dfs(x, y + 1, num + 1, index);
} else {// 如果是最后一列
dfs(pai[index + 1], 0, num + 1, index + 1);
}
}
}
// 是否重复
// true代表没重复,false代表重复
public static boolean isRepeated(int x, int y, int c) {
//呜呜呜不要写循环
// for (int i = 0; i < 9; i++) {
// if (map[i][y] == c) {// 判断一列是否重复
// return false;
// }
// if (map[x][i] == c) {// 判断一行是否重复
// return false;
// }
// }
//尽量写O(1)
if(hang[x][c-1] != 0) {//第x行有数字c
return false;
}
if (lie[y][c-1] != 0) {//第y列有数字c
return false;
}
// 判断所在方格是否重复
//其实判断九宫格是否重复也可以用数组,但我不会!!!
int xx = x / 3;// 行
int yy = y / 3;// 列
for (int i = 3 * xx; i <= 3 * xx + 2; i++) {
for (int j = 3 * yy; j <= 3 * yy + 2; j++) {
if (map[i][j] == c) {
return false;
}
}
}
return true;
}
//计算和
public static int calc(int[][] arr) {
int sum = 0;
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
sum = sum + arr[i][j] * score[i][j];
}
}
return sum;
}
}