去题面的传送门
QAQ这道题已经调了很久了,一直是TLE
第一次交,codevs 35分,洛谷 70分(好吧看到这样子我还以为洛谷的数据水来着)
后来加了一个优化,codevs 95分,洛谷 80分(这才知道其实是codevs数据水)
优化是这样的:
一开始读入之后,把所有没有填的空位,它的所在行、列、九宫格里的已经填了的数字统计一下,放到结构体里,然后按照数字数量由大到小排序(所在区域已经填了的数字越多,越优先搜索),dfs时按照结构体顺序搜索,改掉原来一行一行地搜索
但是这样肯定不行,因为我还是T了(╮(╯▽╰)╭)
想想这样做有什么缺点:
①只统计了数字个数,而不是已经填了的数字的种类
②随着搜索的进行,下一个搜索的最优目标可能会改变,这时,初始时设定的已经排列好的顺序就不是最优的了,还是会有很多冗杂的操作
所以,最后的优化就是,在搜索过程中,用函数计算下一个要搜索的点(其实是启发式搜索的启发函数)。计算的标准就是,改点所在的行、列、九宫格内,还没有没使用的数字的数量,越少越好
(虽然一开始觉得这样很暴力,但是可以让搜索树变得超级小)
一开始T掉的点跑了大约6秒(手动测),优化之后变成717ms,时间限制是4s
ps:启发式搜索还是要选好启发函数
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int ans,cnt,sum;
int ju[10][10],X[10][10],Y[10][10],S[10][10];
bool vis[10][10],used[10][10];
bool flg=0;
struct hh
{
int x,y;
};
int muti[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}};
hh getit()
{
hh ret;
int cm=15,sum=0;
for(int i=1;i<=9;++i)
for(int j=1;j<=9;++j)
if(!ju[i][j])
{
sum=0;
int wh=(i-1)/3*3+(j-1)/3+1;
for(int k=1;k<=9;++k)
if(!X[i][k]&&!Y[j][k]&&!S[wh][k])
sum++;
if(sum<cm)
{
cm=sum;
ret.x=i;
ret.y=j;
}
}
return ret;
}
void dfs(int x,int y,int k,int tot)
{
if(x>=1&&y<=9&&x<=9&&y>=1&&!vis[x][y]&&!used[x][y])
{
int wh=(x-1)/3*3+(y-1)/3+1;
for(int i=1;i<=9;++i)
if(!X[x][i]&&!Y[y][i]&&!S[wh][i])
{
X[x][i]=Y[y][i]=S[wh][i]=vis[x][y]=1;
ju[x][y]=i;
if(k==cnt) ans=max(ans,tot+i*muti[x][y]);
else
{
hh xx=getit();
dfs(xx.x,xx.y,k+1,tot+ju[x][y]*muti[x][y]);
}
ju[x][y]=0;
X[x][i]=Y[y][i]=S[wh][i]=vis[x][y]=0;
}
}
}
int main()
{
for(int i=1;i<=9;++i)
for(int j=1;j<=9;++j)
{
scanf("%d",&ju[i][j]);
if(!ju[i][j])
{
cnt++;
continue;
}
sum+=ju[i][j]*muti[i][j];
used[i][j]=vis[i][j]=true;
int wh=(i-1)/3*3+(j-1)/3+1;
X[i][ju[i][j]]=S[wh][ju[i][j]]=Y[j][ju[i][j]]=1;
}
hh xx=getit();
dfs(xx.x,xx.y,1,sum);
if(ans==0) ans--;
printf("%d",ans);
return 0;
}