[DFS] P1074 靶形数独(提高+)

Date:2019/10/19
Degree of difficulty:提高+
Original question:P1074 靶形数独(提高+)
→ b e g i n \to begin begin
在这里插入图片描述
在这里插入图片描述

这道题和上一个数独的题基本思想是一样的,但是注意几个问题

  1. 怎样记录分数?
  2. 怎样剪枝?

记录分数

这里我们还是用了一个打表的数组(和记录是哪个格子一样的方法)
~~ 捂脸 ~~

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

有点长不要介意

主要分为以下部分

  1. 定义数组,变量部分,
  2. 这里有两个打表的数组,分别记录了地图上所属的格子和所代表的分数;一个结构体(用于邻接表,外加结构体sort排序用的cmp函数)
  3. 一个算分数的函数,dfs函数
  4. 主函数中输入部分,找最少填数的部分,做成邻接表
#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} Happyending

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值