您的位置:首页 > 其它

洛谷 1074 [NOIP2009] 靶形数独 dfs+剪枝

2017-09-23 22:07 295 查看
题目:

https://www.luogu.org/problem/show?pid=1074

自己的代码70分(玄学倒搜);

改不出来看题解,长见识;

70分

正搜40,倒搜70,codevs卡时95!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int ma[10][10],ans=-1,cnt,sum[10][10];
bool can(int x,int y,int now)
{
int fx=0,fy=0;
if(x<=3) fx=1;
else if(x<=6) fx=4;
else fx=7;
if(y<=3) fy=1;
else if(y<=6) fy=4;
else fy=7;
for(int i=1;i<=9;i++)
if(ma[x][i]==now || ma[i][y]==now) return false;
for(int i=fx;i<=fx+2;i++)
for(int j=fy;j<=fy+2;j++)
if(ma[i][j]==now) return false;
return true;
}
void dfs(int x,int tot)
{
int fx=0,fy=0;
if(x==0)
{
ans=max(ans,tot);
return;
}
if(x%9==0) fx=x/9;
else fx=x/9+1;
fy=x-(fx-1)*9;
if(ma[fx][fy]) dfs(x-1,tot+sum[fx][fy]*ma[fx][fy]);
else for(int i=1;i<=9;i++)
{
if(can(fx,fy,i))
{
ma[fx][fy]=i;
dfs(x-1,tot+sum[fx][fy]*ma[fx][fy]);
ma[fx][fy]=0;
}
}
return;
}
void ji()
{
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
sum[i][j]+=6;
for(int i=2;i<=8;i++)
for(int j=2;j<=8;j++) sum[i][j]+=1;
for(int i=3;i<=7;i++)
for(int j=3;j<=7;j++) sum[i][j]+=1;
for(int i=4;i<=6;i++)
for(int j=4;j<=6;j++) sum[i][j]+=1;
sum[5][5]++;
return;
}
void solve()
{
int ss[10][10];
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++) scanf("%d",&ma[i][j]);
ji();
dfs(81,0);
return;
}
int main()
{
solve();
cout<<ans;
return 0;
}


正解:

启发式搜索+状压;

sum_hang[i] : 第i行大于0的数的个数;

lie[i]:第i列使用的数(状压);

hang[i]:第i行使用的数(状压);

ge[i]:第i个九宫个使用的数(状压);

ma:输入;

sum:得分;

un:没有填的格子的位置;

cnt:没填的格子个数;

思路:

预处理;

跳着搜每个没有填的位置,若全搜完即得到一个解;

这里的启发函数的意义:优先搜候选数少的格子,可以减少搜索树的大小;

状压:

对于某列111111001表示7,8个空没有填数;算是卡常吧;

收获:

1. |的回溯用^;

2. 搜索可以跳着搜……;

3. 重载运算符的新姿势;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN=1001,tmp=(1<<9)-1;
int sum_hang[MAXN],lie[MAXN],hang[MAXN],ge[MAXN],ma[MAXN][MAXN],sum[MAXN][MAXN];
int cnt,ans=-1,tot,num;
struct hh
{
int x,y;
bool operator < (hh t) const
{
if(sum_hang[x]==sum_hang[t.x]) return x < t.x;
return sum_hang[x]>sum_hang[t.x];
}
}un[MAXN];
void ji()
{
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
sum[i][j]+=6;
for(int i=1;i<8;i++)
for(int j=1;j<8;j++) sum[i][j]+=1;
for(int i=2;i<7;i++)
for(int j=2;j<7;j++) sum[i][j]+=1;
for(int i=3;i<6;i++)
for(int j=3;j<6;j++) sum[i][j]+=1;
sum[4][4]++;
return;
}
int gets(int x,int y)
{
return (x/3*3)+(y/3);//第几个九宫格;
}
void dfs(int cur,int tot)
{
if(cur>cnt)
{
ans=max(ans,tot);
return;
}
int x=un[cur].x,y=un[cur].y;
int temp=(hang[x]|(lie[y]|ge[gets(x,y)]));//寻找可用的数,简洁迅速;
if(temp==tmp) return;
for(int i=1;i<=9;i++)
{
if(!((temp>>(i-1))&1))
{
int ss=1<<(i-1);
ma[x][y]=i;
hang[x]|=ss;
lie[y]|=ss;
ge[gets(x,y)]|=ss;
dfs(cur+1,tot+ma[x][y]*sum[x][y]);
hang[x]^=ss;//回溯用^;
lie[y]^=ss;
ge[gets(x,y)]^=ss;
ma[x][y]=0;
}
}
return;
}
void solve()
{
ji();
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
{
scanf("%d",&ma[i][j]);
if(ma[i][j])
{
num+=ma[i][j]*sum[i][j];
hang[i]|=(1<<(ma[i][j]-1));
lie[j]|=(1<<(ma[i][j]-1));
ge[gets(i,j)]|=(1<<(ma[i][j]-1));
sum_hang[i]++;
}
}
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
if(!ma[i][j]) cnt++,un[cnt].x=i,un[cnt].y=j;
sort(un+1,un+cnt+1);//优先级排序;
dfs(1,num);
cout<<ans;
}
int main()
{
solve();
return 0;
}


好像可以用跳舞链做(也就比这份代码快一点点),但是不会,%会的dalao;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: