1. 程式人生 > >靶型數獨 洛谷P1074

靶型數獨 洛谷P1074

題目描述

小城和小華都是熱愛數學的好學生,最近,他們不約而同地迷上了數獨遊戲,好勝的他們想用數獨來一比高低。但普通的數獨對他們來說都過於簡單了,於是他們向 Z 博士請教,Z 博士拿出了他最近發明的“靶形數獨”,作為這兩個孩子比試的題目。

靶形數獨的方格同普通數獨一樣,在 99 格寬×99 格高的大九宮格中有9 9 個 33 格寬×33 格高的小九宮格(用粗黑色線隔開的)。在這個大九宮格中,有一些數字是已知的,根據這些數字,利用邏輯推理,在其他的空格上填入 11 到 9 9的數字。每個數字在每個小九宮格內不能重複出現,每個數字在每行、每列也不能重複出現。但靶形數獨有一點和普通數獨不同,即每一個方格都有一個分值,而且如同一個靶子一樣,離中心越近則分值越高。(如圖)

上圖具體的分值分佈是:最裡面一格(黃色區域)為 1010 分,黃色區域外面的一圈(紅色區域)每個格子為 9 9分,再外面一圈(藍色區域)每個格子為 88 分,藍色區域外面一圈(棕色區域)每個格子為 7 7分,最外面一圈(白色區域)每個格子為 6 6分,如上圖所示。比賽的要求是:每個人必須完成一個給定的數獨(每個給定數獨可能有不同的填法),而且要爭取更高的總分數。而這個總分數即每個方格上的分值和完成這個數獨時填在相應格上的數字的乘積的總和

總分數即每個方格上的分值和完成這個數獨時填在相應格上的數字的乘積的總和。如圖,在以下的這個已經填完數字的靶形數獨遊戲中,總分數為 2829。遊戲規定,將以總分數的高低決出勝負。

由於求勝心切,小城找到了善於程式設計的你,讓你幫他求出,對於給定的靶形數獨,能夠得到的最高分數。

輸入輸出格式

輸入格式: 一共 99 行。每行 9 9個整數(每個數都在 0-90−9 的範圍內),表示一個尚未填滿的數獨方格,未填的空格用“00”表示。每兩個數字之間用一個空格隔開。

輸出格式: 輸出共 11 行。輸出可以得到的靶形數獨的最高分數。如果這個數獨無解,則輸出整數-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%的資料,數獨中非0 0數的個數不少於 2424。

NOIP 2009 提高組 第四題

分析

dfs(不打表),用一個序列s[ ]儲存要填的點,dfs攜帶的引數就是要填寫的點的座標: s[ i ][0]與s[ i ][1]存點的座標,s[ i ][2]存點的分值,s[ i ][3]存點的所在宮(我用函式現生成,避免了打表)。 需要注意的有如下幾點: 一、dfs過程中就判斷數能不能放,別放到最後判斷(這一條可能是廢話)實現方法用三個陣列分別存各行、列、宮每個數字的狀態(0表示沒填過,1表示填過) (重要)二、dfs層數與0的個數有關,層數太多就TLE了,我們知道,一行中填過的數字越多,需要填的數越少,就意味著dfs層數越少!所以,我們先填0的數量少的行。(詳細實現見程式碼) 不懂的看這:) 0 0 0 0 0 0 0 0 0 //這一行有9個0 1 0 0 0 0 5 9 0 0 //這一行有6個0 0 0 0 2 0 0 0 8 0 //這一行有7個0 0 0 5 0 2 0 0 0 3 //這一行有6個0 0 0 0 0 0 0 6 4 8 //這一行有6個0 4 1 3 0 0 0 0 0 0 //這一行有6個0 0 0 7 0 0 2 0 9 0 //這一行有6個0 2 0 1 0 6 0 8 0 4 //這一行有4個0 0 8 0 5 0 4 0 1 2//這一行有4個0 在這個例子中,從第一行到第九行dfs的話,那麼dfs第一層就有9種情況!!!根據dfs的原理,從第一層9種情況開始拓展,那麼時間就要花很多。而若從第8層開始只有4種情況,搜尋需要的時間就大大減少。TLE再見!!

程式碼

/*dfs(不打表),用一個序列s[ ]儲存要填的點,dfs攜帶的引數就是要填寫的點的座標:
s[ i ][0]與s[ i ][1]存點的座標,s[ i ][2]存點的分值,s[ i ][3]存點的所在宮(我用函式現生成,避免了打表)。
需要注意的有如下幾點:
一、dfs過程中就判斷數能不能放,別放到最後判斷(這一條可能是廢話)實現方法用三個陣列分別存各行、列、宮每個數字的狀態(0表示沒填過,1表示填過)
(重要)二、dfs層數與0的個數有關,層數太多就TLE了,我們知道,一行中填過的數字越多,需要填的數越少,就意味著dfs層數越少!所以,我們先填0的數量少的行。(詳細實現見程式碼)
不懂的看這:)
0 0 0 0 0 0 0 0 0    //這一行有9個0
1 0 0 0 0 5 9 0 0  //這一行有6個0
0 0 0 2 0 0 0 8 0 //這一行有7個0
0 0 5 0 2 0 0 0 3 //這一行有6個0
0 0 0 0 0 0 6 4 8 //這一行有6個0
4 1 3 0 0 0 0 0 0 //這一行有6個0
0 0 7 0 0 2 0 9 0 //這一行有6個0
2 0 1 0 6 0 8 0 4 //這一行有4個0
0 8 0 5 0 4 0 1 2//這一行有4個0
在這個例子中,從第一行到第九行dfs的話,那麼dfs第一層就有9種情況!!!根據dfs的原理,從第一層9種情況開始拓展,那麼時間就要花很多。而若從第8層開始只有4種情況,搜尋需要的時間就大大減少。TLE再見!!
*/
#include<bits/stdc++.h>
using namespace std;
const int N=102;
struct bz
{
	int rank,sum;
}cou[N];
bool hang[N][N],lie[N][N],gong[N][N];
int s[N][N],u,ok,most=-1,have,a[N][N];
int point(int i,int j)//給出兩個整型變數代表座標,返回此座標的所在宮
{
    if(i==1||j==1||i==9||j==9)   return 6;
    if(i==2||j==2||i==8||j==8)     return 7;
    if(i==3||j==3||i==7||j==7)   return 8;
    if(i==4||j==4||i==6||j==6)   return 9;
    return 10;
}
int which(int i,int j)//給出兩個整型變數代表座標,返回此座標的所在宮
{
    if(i<=3)
    {
        if(j<=3)        return 1;
        else if(j<=6)   return 2;
        else            return 3;
    }
    else if(i<=6)
    {
        if(j<=3)        return 4;
        else if(j<=6)    return 5;
        else            return 6;
    }
    else
    {
        if(j<=3)        return 7;
        else if(j<=6)   return 8;
        else            return 9;
    }
}
bool cmp(bz x,bz y)
{
	return x.sum<y.sum;
}
void dfs(int p,int score)
{
	if(p==u)
	{
		most=max(most,score);
		return;
	}
	for(int i=1;i<=9;i++)
	 if(!hang[s[p][0]][i]&&!lie[s[p][1]][i]&&!gong[s[p][3]][i])//判斷可不可以將i填入 
	 {
	 	hang[s[p][0]][i]=lie[s[p][1]][i]=gong[s[p][3]][i]=1;
	 	dfs(p+1,score+(s[p][2]*i));
	 	hang[s[p][0]][i]=lie[s[p][1]][i]=gong[s[p][3]][i]=0;
	 }
}
int main()
{
	for(int i=1;i<=9;i++)//rank存其初始行號,排序後就不會丟失
	 cou[i].rank=i;
	for(int i=1;i<=9;i++)
	 for(int j=1;j<=9;j++)
	 {
	 	cin>>a[i][j];
	 	if(a[i][j]>0)//排序,0少的在前
	 	 hang[i][a[i][j]]=lie[j][a[i][j]]=gong[which(i,j)][a[i][j]]=1,have+=a[i][j]*point(i,j);
	 	else cou[i].sum++;//是0就計數 
	 }
	sort(cou+1,cou+10,cmp);
	for(int i=1;i<=9;i++)
	 for(int j=1;j<=9;j++)
	  if(a[cou[i].rank][j]==0)//先搜0少的行 ,預處理 
	   s[u][0]=cou[i].rank,s[u][1]=j,s[u][2]=point(cou[i].rank,j),s[u++][3]=which(cou[i].rank,j);
    dfs(0,have);
    cout<<most;
}