专栏文章
题解:P1074 [NOIP2009 提高组] 靶形数独
P1074题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miqg3oa6
- 此快照首次捕获于
- 2025/12/04 04:14 3 个月前
- 此快照最后确认于
- 2025/12/04 04:14 3 个月前
题目传送门
思路
本题要求出数独的所有解法,在所有解中找最大值,所以搜索量巨大。显然的优化思路是优先选择限制条件多的位置填写。比如某行已经填了 个数,那么还剩 个格子,只有 种情况。可以把搜索过程想象成一棵树,根节点是起点,这样做可以使刚开始的分叉少,之后的分叉多,一旦发生剪枝,减去的节点就更多,剪枝的效果就更加显著。这个剪枝原则适用于所有数独问题,虽然看起来代码里浪费了时间去找已填数最多的行和列,但实际上浪费的时间相比剪枝节省的时间,微不足道。
代码
CPP#include<bits/stdc++.h>
using namespace std;
const int 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 row[10][10],col[10][10],grid[10][10],a[10][10];
int row_cnt[10],col_cnt[10],cnt,ans=-1;
int tran(int x,int y){
return (x-1)/3*3+(y-1)/3+1;
}
int calc(){
int tmp=0;
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
tmp+=score[i][j]*a[i][j];
return tmp;
}
void dfs(int x,int y,int tot){
if(tot==81){
ans=max(ans,calc());
return;
}
for(int i=1;i<=9;i++){
if(row[x][i]||col[y][i]||grid[tran(x,y)][i])continue;
row[x][i]=col[y][i]=grid[tran(x,y)][i]=true;
row_cnt[x]++,col_cnt[y]++;
a[x][y]=i;
int tmpr=-1,nxt_x=0,tmpc=-1,nxt_y=0;
for(int j=1;j<=9;j++)
if(row_cnt[j]>tmpr&&row_cnt[j]<9)
tmpr=row_cnt[j],nxt_x=j;
for(int j=1;j<=9;j++)
if(col_cnt[j]>tmpc&&!(a[nxt_x][j]))
tmpc=col_cnt[j],nxt_y=j;
dfs(nxt_x,nxt_y,tot+1);
row[x][i]=col[y][i]=grid[tran(x,y)][i]=false;
row_cnt[x]--,col_cnt[y]--;
a[x][y]=0;
}
}
int main(){
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
cin>>a[i][j];
if(a[i][j]!=0){
row[i][a[i][j]]=true;
col[j][a[i][j]]=true;
grid[tran(i,j)][a[i][j]]=true;
row_cnt[i]++,col_cnt[j]++;
cnt++;//已经填了的数量
}
}
}
int tmpr=-1,x,tmpc=-1,y;
for(int j=1;j<=9;j++)
if(row_cnt[j]>tmpr&&row_cnt[j]<9)
tmpr=row_cnt[j],x=j;//选已填数最多的行x出发
for(int j=1;j<=9;j++)
if(col_cnt[j]>tmpc&&(!a[x][j]))
tmpc=col_cnt[j],y=j;//选行x里填数最多的列y出发
dfs(x,y,cnt);
cout<<ans;
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...