小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。
靶形数独的方格同普通数独一样,在 99 格宽×99 格高的大九宫格中有9 9 个 33 格宽×33 格高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 11 到 9 9的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。(如图)
上图具体的分值分布是:最里面一格(黄色区域)为 1010 分,黄色区域外面的一圈(红色区域)每个格子为 9 9分,再外面一圈(蓝色区域)每个格子为 88 分,蓝色区域外面一圈(棕色区域)每个格子为 7 7分,最外面一圈(白色区域)每个格子为 6 6分,如上图所示。比赛的要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和
总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为 2829。游戏规定,将以总分数的高低决出胜负。
由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。
在9*9的矩阵内,每一个点都有一个分数,你先在需要求出一个数独使得数独上的每一个点乘这个点的分数之和最大,并输出这一个分数。
这一道题目的难点就是求解数独的过程,我们需要对整一个搜索树进行剪枝。
如果我们需要对整一个问题的时间复杂度进行优化,我们需要:
然后就需要进行常数优化惹:
现在回到找最小点这一个问题上:
#include
using namespace std;
bool flag;
int x[200];
int y[200];
int n[200];
int tot[300];
int num[1000000];
char s[300][300];
int nine[100][100]=
{
{0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9}
};
void print(void)
{
for (int i=1;i<=9;++i)
{
for (int j=1;j<=9;++j)
cout<<s[i][j];
}
puts("");
}
void change(int i,int j,int v)
{
x[i]^=(1<<v-1);
y[j]^=(1<<v-1);
n[nine[i][j]]^=(1<<v-1);
}
bool dfs(int D)
{
if (D==0) return 1;
int Min=INT_MAX,xx=0,yy=0,temp;
for (int i=1;i<=9;++i)
for (int j=1;j<=9;++j)
if (s[i][j]=='.')
{
int now=x[i]&y[j]&n[nine[i][j]];
if (now==0) return 0;
if (tot[now]<Min)
{
Min=tot[now];
xx=i;
yy=j;
temp=now;
}
}
for (int i=temp;i;i-=(i&-i))
{
int low=i&-i;
int k=num[low];
change(xx,yy,k);
s[xx][yy]=k+'0';
if (dfs(D-1)) return 1;
change(xx,yy,k);
s[xx][yy]='.';
}
return 0;
}
int main(void)
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
for (int i=0;i<(1<<10);++i)
for (int j=i;j;j-=(j&-j))
tot[i]++;
for (int i=1;i<10;++i)
num[1<<i-1]=i;
//累计每一个数lowbit值中的1的个数
cin>>s[1][1];
up:
flag=0;
for (int i=1;i<=9;++i)
for (int j=(i==1?2:1);j<=9;++j)
cin>>s[i][j];
for (int i=1;i<=9;++i)
x[i]=y[i]=n[i]=(1<<9)-1;
//初始化:将每一个二进制位改成全1
int sum=0;
for (int i=1;i<=9;++i)
for (int j=1;j<=9;++j)
if (s[i][j]!='.')
{
int v=s[i][j]-'0';
change(i,j,v);
}
else sum++;
dfs(sum);
print();
cin>>s[1][1];
if (s[1][1]!='e') goto up;
return 0;
}