洛谷 P1074 靶形數獨(剪枝)
//人生中第一道藍題(3.5h)
題目描述
小城和小華都是熱愛數學的好學生,最近,他們不約而同地迷上了數獨遊戲,好勝的他們想用數獨來一比高低。但普通的數獨對他們來說都過於簡單了,於是他們向 Z 博士請教,Z 博士拿出了他最近發明的“靶形數獨”,作為這兩個孩子比試的題目。
靶形數獨的方格同普通數獨一樣,在 9 格寬×9 格高的大九宮格中有9 個 3 格寬3×3 格高的小九宮格(用粗黑色線隔開的)。在這個大九宮格中,有一些數字是已知的,根據這些數字,利用邏輯推理,在其他的空格上填入 1 到 9的數字。每個數字在每個小九宮格內不能重復出現,每個數字在每行、每列也不能重復出現。但靶形數獨有一點和普通數獨不同,即每一個方格都有一個分值,而且如同一個靶子一樣,離中心越近則分值越高。(如圖)
上圖具體的分值分布是:最裏面一格(黃色區域)為 10 分,黃色區域外面的一圈(紅色區域)每個格子為9分,再外面一圈(藍色區域)每個格子為8 分,藍色區域外面一圈(棕色區域)每個格子為7分,最外面一圈(白色區域)每個格子為6分,如上圖所示。比賽的要求是:每個人必須完成一個給定的數獨(每個給定數獨可能有不同的填法),而且要爭取更高的總分數。而這個總分數即每個方格上的分值和完成這個數獨時填在相應格上的數字的乘積的總和
總分數即每個方格上的分值和完成這個數獨時填在相應格上的數字的乘積的總和。如圖,在以下的這個已經填完數字的靶形數獨遊戲中,總分數為 2829。遊戲規定,將以總分數的高低決出勝負。
由於求勝心切,小城找到了善於編程的你,讓你幫他求出,對於給定的靶形數獨,能夠得到的最高分數。
輸入輸出格式
輸入格式:
一共 9 行。每行9個整數(每個數都在 0?9 的範圍內),表示一個尚未填滿的數獨方格,未填的空格用“0”表示。每兩個數字之間用一個空格隔開。
輸出格式:
輸出共 1 行。輸出可以得到的靶形數獨的最高分數。如果這個數獨無解,則輸出整數-1。
輸入輸出樣例
輸入樣例#1:7 0 0 9 0 0 0 0 1
1 0 0 0 0 5 9 0 0
0 0 0 2 0 0 0 8 0
0 0 5 0 2 0 0 0 3
0 0 0 0 0 0 6 4 8
4 1 3 0 0 0 0 0 0
0 0 7 0 0 2 0 9 0
2 0 1 0 6 0 8 0 4
0 8 0 5 0 4 0 1 2
輸出樣例#1: 2829
輸入樣例#2: 0 0 0 7 0 2 4 5 3
9 0 0 0 0 8 0 0 0
7 4 0 0 0 5 0 1 0
1 9 5 0 8 0 0 0 0
0 7 0 0 0 0 0 2 5
0 3 0 5 7 9 1 0 8
0 0 0 6 0 1 0 0 0
0 6 0 9 0 0 0 0 1
0 0 0 0 0 0 0 0 6
輸出樣例#2: 2852
說明
【數據範圍】
40%的數據,數獨中非 00 數的個數不少於3030。
80%的數據,數獨中非 00 數的個數不少於2626。
100%的數據,數獨中非00數的個數不少於2424。
NOIP 2009 提高組 第四題
解題思路:
搜索,用一個數組s保存要存的點,其中s[ i ][0]與s[ i ][1]存點的坐標,s[ i ][2]存點在什麽顏色上,s[ i ][3]存點的所在宮,用三個布爾數組分別記錄當前行、列、宮中已經放了哪些數,如果這樣搜會TLE,所以我們要剪枝,因為dfs層數與0的個數有關,層數太多就TLE了,我們知道,一行中填過的數字越多,需要填的數越少,就意味著dfs層數越少!所以,我們先填0的數量少的行。
AC代碼:
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 struct kkk { 5 int rank,sum = 0;//rank表示行號,sum表示這行0的個數 6 }e[10]; 7 int a[10][10],s[100][4],bj = 1,ans,daan = -1;//bj記錄0的個數,daan為最終答案 8 bool h[10][10],l[10][10],g[10][10];//h,l,g分別表示行列宮的放置情況 9 int find(int i,int j) {//找當前點在哪個宮 10 if(i <= 3) { 11 if(j <= 3) return 1; 12 else if(j <= 6) return 2; 13 else return 3; 14 } 15 else if(i <= 6) { 16 if(j <= 3) return 4; 17 else if(j <= 6) return 5; 18 else return 6; 19 } 20 else { 21 if(j <= 3) return 7; 22 else if(j <= 6) return 8; 23 else return 9; 24 } 25 } 26 int score(int i,int j) {//判斷當前點在哪個顏色上 27 if(i == 1 || j == 1 || i == 9 || j == 9) return 6; 28 if(i == 2 || j == 2 || i == 8 || j == 8) return 7; 29 if(i == 3 || j == 3 || i == 7 || j == 7) return 8; 30 if(i == 4 || j == 4 || i == 6 || j == 6) return 9; 31 return 10; 32 } 33 bool cmp(kkk d,kkk b) {//根據每行0的個數排序 34 return d.sum < b.sum; 35 } 36 void dfs(int b,int k) {//表示在搜s[b],當前分數為k 37 if(b == bj) {//合法填完了所有數 38 if(k > daan) daan = k;//更新答案 39 return ; 40 } 41 for(int i = 1;i <= 9; i++) { 42 if(!h[s[b][0]][i] && !l[s[b][1]][i] && !g[s[b][3]][i]) {//如果當前行,列,宮沒有放過這個數 43 h[s[b][0]][i] = l[s[b][1]][i] = g[s[b][3]][i] = 1;//標記 44 dfs(b + 1,k + (s[b][2] * i));//搜索 45 h[s[b][0]][i] = l[s[b][1]][i] = g[s[b][3]][i] = 0;//回溯 46 } 47 } 48 return ; 49 } 50 int main() 51 { 52 for(int i = 1;i <= 9; i++) e[i].rank = i;//初始化行號 53 for(int i = 1;i <= 9; i++) 54 for(int j = 1;j <= 9; j++) { 55 scanf("%d",&a[i][j]); 56 if(a[i][j] > 0){//如果這個數不是0 57 h[i][a[i][j]] = l[j][a[i][j]] = g[find(i,j)][a[i][j]] = 1;//標記當前行,列,宮已經放置了這個數,以後就不能放這個數了 58 ans += a[i][j] * score(i,j);//ans記錄初始化地圖已知數字總分和 59 } 60 else e[i].sum++;//否則這行0個數加一 61 } 62 sort(e+1,e+10,cmp); 63 for(int i = 1;i <= 9; i++) 64 for(int j = 1;j <= 9; j++)//從0的個數少的行開始 65 if(a[e[i].rank][j] == 0) 66 s[bj][0] = e[i].rank,s[bj][1] = j,s[bj][2] = score(e[i].rank,j),s[bj++][3] = find(e[i].rank,j); //記錄圖中0的信息 67 dfs(1,ans); 68 printf("%d",daan); 69 return 0; 70 }
洛谷 P1074 靶形數獨(剪枝)