首页 > 其他分享 >P1074 [NOIP2009 提高组] 靶形数独

P1074 [NOIP2009 提高组] 靶形数独

时间:2023-04-16 18:23:55浏览次数:54  
标签:10 int void NOIP2009 write read 形数 P1074 宫格

题目传送门

思路

就是一个填数独的小游戏
不会填数独的先去自己玩几把
众所周知,数独每一行、每一列、每一个3*3宫格内的数字均含1~9,且不重复
所以我们设三个数组r[10][10]c[10][10]block[10][10]
分别记录行、列、3*3宫格内数字的使用情况
重点:剪枝
我们知道,数独的玩法是先从已知数字多的一行/一列/宫格开始填,这样要枚举的数就少,更好填
剪枝也是这个思路
记录每一行 \(0\) 的个数,然后从小到大排序再dfs,这样dfs的层数会少很多
数据比较小,所以可以只按行排就行了

然后就是最简单的搜索与回溯啦

代码

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define pc putchar
using namespace std;
template<typename T>
inline void read(T &x)
{
    x=0;
    T f=1;
    char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1; c=getchar();}
    while(isdigit(c)){x=x*10+(c^48); c=getchar();}
    x*=f;
    return;
}
template<typename T,typename... Args> inline void read(T &x, Args&... args){read(x); read(args...);}
template<typename Z>
inline void write(Z x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar((x%10)^48);
}
template<typename Z,typename... Args> inline void write(Z x, Args... args){write(x); putchar(' '); write(args...);}
const int N=10;
const int val[N][N] = {//方格分值
    {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 id(int x,int y)//看哪个宫格需要计算一下
{
    if(x<=3 && y<=3) return 1;
    if(x<=3 && y<=6) return 2;
    if(x<=3 && y<=9) return 3;
    if(x<=6 && y<=3) return 4;
    if(x<=6 && y<=6) return 5;
    if(x<=6 && y<=9) return 6;
    if(x<=9 && y<=3) return 7;
    if(x<=9 && y<=6) return 8;
    if(x<=9 && y<=9) return 9;
}
struct zer{
    int line, cnt;
    friend bool operator<(const zer& x, const zer& y){
	return x.cnt < y.cnt;
    }
}z[10];
int a[N][N];
ll ans;
bool r[N][N],c[N][N],block[N][N];//分别记录行、列、`3*3`宫格内数字的使用情况
void dfs(int pos,int x,int y)
{
    if(pos == 10)//全搜完了
    {
	ll sum = 0;
	for(int i=1;i<=9;i++)
            for(int j=1;j<=9;j++)
		sum += a[i][j] * val[i][j];//计算当前的数独的分数
	ans = max(ans, sum);//取最大
	return;
    }
    if(y == 10)
    {
	dfs(pos+1, z[pos+1].line, 1);//这一行填完了,递归到下一行
	return;
    }
    if(a[x][y] == 0)//这个位置没填数
    {
	for(int num=1;num<=9;num++)
	    if(!r[x][num] && !c[y][num] && !block[ id(x,y) ][num])//可以填
	    {
                r[x][num] = c[y][num] = block[ id(x,y) ][num] = 1;
		a[x][y] = num;
		dfs(pos, x, y+1);
		a[x][y] = 0;//回溯
		r[x][num] = c[y][num] = block[ id(x,y) ][num] = 0;
            }
    }
    else //这个位置填上了,去下一个数
	dfs(pos, x, y+1);
}
int main()
{
    for(int i=1;i<=9;i++)
    {
	int cnt0 = 0;
	for(int j=1;j<=9;j++)
	{
		read(a[i][j]);
		if(a[i][j] == 0) cnt0++;//一行 0 的个数
		r[i][a[i][j]] = 1;
		c[j][a[i][j]] = 1;
		block[ id(i,j) ][a[i][j]] = 1;
	}
	z[i] = {i, cnt0};
    }
    sort(z+1, z+10);//根据 0 的数量排序
    dfs(1, z[1].line, 1);
    write(ans);
    return 0;
}

标签:10,int,void,NOIP2009,write,read,形数,P1074,宫格
From: https://www.cnblogs.com/LittleN/p/17323761.html

相关文章