Date:2019/10/19
Degree of difficulty:提高+
Original question:P1074 靶形数独(提高+)
→
b
e
g
i
n
\to begin
→begin
这道题和上一个数独的题基本思想是一样的,但是注意几个问题
- 怎样记录分数?
- 怎样剪枝?
记录分数
这里我们还是用了一个打表的数组(和记录是哪个格子一样的方法)
~~ 捂脸 ~~
const ll score[10][10]={
{0,0,0,0,0,0,0,0,0,0},
{0,6,6,6,6,6,6,6,6,6},
{0,6,7,7,7,7,7,7,7,6},
{0,6,7,8,8,8,8,8,7,6},
{0,6,7,8,9,9,9,8,7,6},
{0,6,7,8,9,10,9,8,7,6},
{0,6,7,8,9,9,9,8,7,6},
{0,6,7,8,8,8,8,8,7,6},
{0,6,7,7,7,7,7,7,7,6},
{0,6,6,6,6,6,6,6,6,6},
};
怎样剪枝
我们这个题按照朴素的做法,从1,1
循环搜索到9,9
也是可以的,但只是拿不到AC,所以这时候需要考虑剪枝
我们想一下我们做数独的时候,总是从哪里开始的呢?
当然是从0最少的那一行/那一列/那一格 开始的
所以我们就想到了一种有效的剪枝方法
实现方法是邻接表
(Next数组的妙用系列)
按照含有0的数目顺序依次搜索
这样就可减少很多时间
for(int i =1; i <= 9; i++){
ze[i].rplace=i;
}
sort(ze+1,ze+10,cmp);//按照含有0的数目排序
for(int i = 1; i<= 8; i++){
next[ze[i].rplace]=ze[i+1].rplace; //next数组的妙用系列
}
next[ze[9].rplace]=10; //用一个相当于邻接表的东西存起来从高到低的最少填数顺序
dfs(ze[1].rplace,1);//从空着数最少的那条边开始搜索
AC code
有点长不要介意
主要分为以下部分
- 定义数组,变量部分,
- 这里有两个打表的数组,分别记录了地图上所属的格子和所代表的分数;一个结构体(用于邻接表,外加结构体sort排序用的cmp函数)
- 一个算分数的函数,dfs函数
- 主函数中输入部分,找最少填数的部分,做成邻接表
#include<bits/stdc++.h>
#include<algorithm>
#define ll long long
#define F(a,b) for(int i=a; i<=b; i++)
using namespace std;
//定义区----------------------------------------------------------------------
int vis[11][11]={
{0,0,0,0,0,0,0,0,0,0},//1
{0,1,1,1,2,2,2,3,3,3},//2
{0,1,1,1,2,2,2,3,3,3},//3
{0,1,1,1,2,2,2,3,3,3},//4
{0,4,4,4,5,5,5,6,6,6},//5
{0,4,4,4,5,5,5,6,6,6},//6
{0,4,4,4,5,5,5,6,6,6},//7
{0,7,7,7,8,8,8,9,9,9},//8
{0,7,7,7,8,8,8,9,9,9},//9
{0,7,7,7,8,8,8,9,9,9},//10
};
const ll score[10][10]={
{0,0,0,0,0,0,0,0,0,0},
{0,6,6,6,6,6,6,6,6,6},
{0,6,7,7,7,7,7,7,7,6},
{0,6,7,8,8,8,8,8,7,6},
{0,6,7,8,9,9,9,8,7,6},
{0,6,7,8,9,10,9,8,7,6},
{0,6,7,8,9,9,9,8,7,6},
{0,6,7,8,8,8,8,8,7,6},
{0,6,7,7,7,7,7,7,7,6},
{0,6,6,6,6,6,6,6,6,6},
};
int a[10][10],x,fl,next[10];
bool h[11][11],l[11][11],ge[11][11];
ll mx;
struct node{
int rplace,vle;
}ze[20];
bool cmp(node a,node b){
if(a.vle==b.vle) return a.rplace<b.rplace;
return a.vle<b.vle;
}
//功能函数-------------------------------------------------------------------
inline ll Sum(){ //求出整个数独的分数
ll sum=0;
for(ll i=1; i<=9; i++){
for(ll j=1; j<=9; j++){
sum+=a[i][j]*score[i][j];
}
}
return sum;
}
void dfs(int x,int y){
if(x == 10){ //如果x=10,已经填好,结束并比较
mx=max(mx,Sum());
return;
}
if(y == 10){ //换行
dfs(next[x],1);
return;
}
if(a[x][y]!=0){ //判定是否有数,有数就跳过
dfs(x,y+1);
return ;
}
for(int i = 1; i <= 9; i ++){//循环递归数独
if(h[x][i]==0 && l[y][i]==0 && ge[vis[x][y]][i]==0){
a[x][y]=i;
h[x][i]=1;
l[y][i]=1;
ge[vis[x][y]][i]=1;
dfs(x,y+1);
a[x][y]=0;
h[x][i]=0;
l[y][i]=0;
ge[vis[x][y]][i]=0;
}
}
}
//主函数-------------------------------------------------------------------
int main() {
for(int i = 1; i <= 9; i++){//输入并把是0的地方计数
for(int j = 1; j <= 9; j++){
scanf("%d ",&x);
a[i][j]=x;
h[i][x]=1;
l[j][x]=1;
ge[vis[i][j]][x]=1;
if(x==0){
ze[i].vle++;
}
}
}
for(int i =1; i <= 9; i++){
ze[i].rplace=i;
}
sort(ze+1,ze+10,cmp);
for(int i = 1; i<= 8; i++){
next[ze[i].rplace]=ze[i+1].rplace;
}
next[ze[9].rplace]=10; //用一个相当于邻接表的东西存起来从高到低的最少填数顺序
dfs(ze[1].rplace,1);//从空着数最少的那条边开始搜索
if(mx==0) printf("-1");
else printf("%lld",mx);
return 0;
}
H a p p y → e n d i n g {Happy} \to {ending} Happy→ending