NOIP2009靶形数独(DFS)

来源:互联网 发布:网络推广专员面试 编辑:程序博客网 时间:2024/05/21 18:33

NOIP2009靶形数独

思路1

刚看到这题,这不显然DFS么
直接爆搜了先。。。
稍微处理了下,爆搜有80分
改了半天,还没过。。

然后发现有几个货用了一个很污的方法
因为dfs超时,他们在dfs上加了句话
if(++T>=12000000) return ;
因为是在更新ans的值,这样做有可能过,而且在超时前强行return掉
(估计这个极限数字他们也试了好久)
由于数据比较水,这样还真过了,800毫秒卡了过去。。。
好方法!真的
直接超时还不如像这样碰碰运气

题解1

#include<iostream>#include<cstdio>#include<algorithm>using namespace std;#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)#define INF 0x3f3f3f3f#define M 100005#define N 10struct node {int x,y,v;}B[100];bool cmp(node x,node y) {return x.v<y.v;}inline void chk(int &a,int b) {    if(a<b) a=b;}int sc[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 ans,cnt,T,a;int pic[N][N];bool mark[4][N][N];inline int f(int a,int b) {    a=(a-1)/3;    b=(b-1)/3;    return a*3+b+1;}void dfs(int s,int sum) {    T++;    if(T>=12000000)return ;    if(s==cnt+1) {        chk(ans,sum);        return ;    }    int x=B[s].x;    int y=B[s].y;    int a=f(x,y);    FOR(i,1,9) {        if(mark[1][x][i]||mark[2][y][i]||mark[3][a][i]) continue;        mark[1][x][i]=mark[2][y][i]=mark[3][a][i]=1;        pic[x][y]=i;        dfs(s+1,sum+i*sc[x][y]);        mark[1][x][i]=mark[2][y][i]=mark[3][a][i]=0;        pic[x][y]=0;    }}void Init() {    FOR(i,1,9)FOR(j,1,9) if(pic[i][j]) {        int num=pic[i][j];        mark[1][i][num]=1;        mark[2][j][num]=1;        mark[3][f(i,j)][num]=1;    }    int &sum=a;    FOR(i,1,9)FOR(j,1,9) {        if(pic[i][j]==0) {            cnt++;            FOR(k,1,9)if(mark[1][i][k]+mark[2][j][k]+mark[3][f(i,j)][k]==0)B[cnt].v++;            B[cnt].x=i,B[cnt].y=j;        }        else sum+=pic[i][j]*sc[i][j];    }    sort(B+1,B+1+cnt,cmp);}int main() {    FOR(i,1,9)FOR(j,1,9) scanf("%d",&pic[i][j]);    Init();    dfs(1,a);    if(ans) cout<<ans<<endl;    else puts("-1");    return 0;}

思路2

当然,上述算法显然不是正解
这昨天我在帮同学调这道题的时候
小C说正解是每次dfs都找当前图中可填数字最少的位置来枚举

这样做正确性还是比较显然的
当然我下面这份代码跑的还是比较慢的
再用二进制状压一下,位运算就快多了

题解2

#include<iostream>#include<cstdio>#include<algorithm>using namespace std;#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)#define INF 0x3f3f3f3f#define M 100005#define N 10struct node {int x,y;}B;inline void chk(int &a,int b) {if(a<b) a=b;}int sc[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 ans,cnt,T;int pic[N][N];bool mark[4][N][N];inline int f(int a,int b) {    a=(a-1)/3;    b=(b-1)/3;    return a*3+b+1;}inline node getnxt(){    node tmp;    tmp=(node){0,0};    int MIN=INF;    FOR(i,1,9)FOR(j,1,9) {        if(pic[i][j]==0) {            int cnt=0;            FOR(k,1,9)if(!mark[1][i][k]&&!mark[2][j][k]&&!mark[3][f(i,j)][k])cnt++;            if(cnt<MIN)MIN=cnt,tmp.x=i,tmp.y=j;        }    }    return tmp;}void dfs(int sum) {    node s=getnxt();    if(s.x==0&&s.y==0) {        chk(ans,sum);        return ;    }    int x=s.x;    int y=s.y;    int a=f(x,y);    FOR(i,1,9) {        if(mark[1][x][i]||mark[2][y][i]||mark[3][a][i]) continue;        mark[1][x][i]=mark[2][y][i]=mark[3][a][i]=1;        pic[x][y]=i;        dfs(sum+i*sc[x][y]);        mark[1][x][i]=mark[2][y][i]=mark[3][a][i]=0;        pic[x][y]=0;    }}void Init() {    FOR(i,1,9)FOR(j,1,9) if(pic[i][j]) {        int num=pic[i][j];        mark[1][i][num]=1;        mark[2][j][num]=1;        mark[3][f(i,j)][num]=1;    }}int main() {    FOR(i,1,9)FOR(j,1,9) scanf("%d",&pic[i][j]);    Init();    int sum=0;    FOR(i,1,9)FOR(j,1,9) sum+=pic[i][j]*sc[i][j];    dfs(sum);    if(ans) cout<<ans<<endl;    else puts("-1");    return 0;}
原创粉丝点击