P1074 靶形数独

传送门

刚开始想都没想直接爆搜

然后35

然后试了优先找分值大的点,优先填大的数

发现样例2都过不了

放弃了

考虑怎么剪枝

对于一个点,有多种可能的数

如果可能的数少,那么从这个点下去的分支也会比较少

所以预处理一波

把可以填的点按可以填的数的数量排序一遍

然后按排序后的顺序dfs

然后80...

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int mul[17][17],cnt,ans=-1,sum,mp[17][17];//mul为分值,mp为初始状态
bool hor[17][17],ver[17][17],blk[17][17];//分别存每个行,列和宫可以放的数
struct node
{
    int x,y,num;
    bool pd[17];
    node() {memset(pd,0,sizeof(pd)); x=y=num=0; }
}t[107];//存可以放的点
inline bool cmp(const node a,const node b){return a.num<b.num; }
//按可能的数的数量从小到大排序
inline void dfs(int x,int y,int now,int stp)
{
    //cout<<x<<" "<<y<<" "<<mul[x][y]<<"!";
    if(stp>cnt)
    {
        ans=max(ans,now+sum);
        return;
    }//如果放满就尝试更新答案
    int xx=(x-1)/3,yy=(y-1)/3,z=xx*3+yy+1;
    for(int k=9;k;k--)
    {
        if(hor[x][k]||ver[y][k]||blk[z][k]) continue;//判断重复
        hor[x][k]=ver[y][k]=blk[z][k]=1;
        //更新标记
        dfs(t[stp+1].x,t[stp+1].y,now+mul[x][y]*k,stp+1);//向下一层搜索
        hor[x][k]=ver[y][k]=blk[z][k]=0;
        //清除标记
    }
}
int main()
{
    int a;
    for(int i=1;i<=9;i++)
        for(int j=1;j<=9;j++)
        {
            a=max(abs(5-i),abs(5-j));
            mul[i][j]=10-a;
        }//预处理分值
    for(int i=1;i<=9;i++)
    {
        for(int j=1;j<=9;j++)
        {
            scanf("%d",&a);
            sum+=mul[i][j]*a;
            mp[i][j]=a;
            if(!a) continue;
            int x=(i-1)/3,y=(j-1)/3;
            if(hor[i][a]||ver[j][a]||blk[x*3+y+1][a])
            {
                cout<<-1;
                return 0;
            }//判断冲突
            hor[i][a]=1;
            ver[j][a]=1;
            blk[x*3+y+1][a]=1;
        }
    }//预处理行,列和宫的情况
    for(int i=1;i<=9;i++)
        for(int j=1;j<=9;j++)
        {
            if(mp[i][j]) continue;
            t[++cnt].x=i; t[cnt].y=j;
            for(int k=1;k<=9;k++)
            {
                int x=(i-1)/3,y=(j-1)/3;
                if(hor[i][k]||hor[j][k]||blk[x*3+y+1][k]) t[cnt].pd[k]=1;
            }
            for(int k=1;k<=9;k++)
                if(!t[cnt].pd[k]) t[cnt].num++;
        }//预处理每个点的可以放的数的数量
    sort(t+1,t+cnt+1,cmp);//排序
    dfs(t[1].x,t[1].y,0,1);//按排序后的顺序dfs
    cout<<ans;
    return 0;
}
80分代码

 

不会了...

还能怎么剪枝啊!

根本想不到了好吧

然后就去看了看题解

发现题解只按每一行的可能的数来剪枝...

然后我就懵逼了..

为什么我考虑行列和宫三种情况就TLE了

题解只考虑行的情况能过...

后来仔细想了想

题解按行dfs,如果同一行前面已经放了,那么后面同一行的可能就会更少

而我的是到处乱放,前面放完后比较不容易排除后面的可能

应该是因为这个吧...

反正这样能过,搜索大家都会

然后要预处理一波

看看就懂了,很简单的

附上一个输出预处理结果的函数:

inline void out()
{
    cout<<"______"<<endl;
    for(int i=1;i<=9;i++)
    {
        for(int j=1;j<=9;j++)
            cout<<mul[i][j]<<" ";
        cout<<endl;
    }
    cout<<"______"<<endl;
    for(int i=1;i<=9;i++)
    {
        for(int j=1;j<=9;j++)
        {
            int x=(i-1)/3,y=(j-1)/3;
            cout<<x*3+y+1<<" ";
        }
        cout<<endl;
    }
    cout<<"______"<<endl;
    for(int i=1;i<=9;i++)
    {
        for(int j=1;j<=9;j++)
            if(hor[i][j]) cout<<j<<" ";
        cout<<endl;
    }
    cout<<"______"<<endl;
    for(int i=1;i<=9;i++)
    {
        for(int j=1;j<=9;j++)
            if(ver[i][j]) cout<<j<<" ";
        cout<<endl;
    }
    cout<<"______"<<endl;
    for(int i=1;i<=9;i++)
    {
        for(int j=1;j<=9;j++)
            if(blk[i][j]) cout<<j<<" ";
        cout<<endl;
    }
    cout<<"______"<<endl;
}
out

然后是满分代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int mul[17][17],cnt,ans=-1,sum,mp[17][17];//mul为分值,mp为初始状态
bool hor[17][17],ver[17][17],blk[17][17];//分别存每个行,列和宫可以放的数
struct node
{
    int x,y,num;
    bool pd[17];
    node() {memset(pd,0,sizeof(pd)); x=y=num=0; }
}t[107];//存可以放的点
struct data
{
    int id,num;
    data() {id=num=0; }
}d[17];//存每一行的可能
inline bool cmp(const data a,const data b){return a.num<b.num; }//按每一行的可能排序
inline void dfs(int x,int y,int now,int stp)
{
    if(stp>cnt)
    {
        ans=max(ans,now+sum);
        return;
    }//如果放满就尝试更新答案
    int xx=(x-1)/3,yy=(y-1)/3,z=xx*3+yy+1;
    for(int k=9;k;k--)
    {
        if(hor[x][k]||ver[y][k]||blk[z][k]) continue;//判断重复
        hor[x][k]=ver[y][k]=blk[z][k]=1;
        //更新标记
        dfs(t[stp+1].x,t[stp+1].y,now+mul[x][y]*k,stp+1);//向下一层搜索
        hor[x][k]=ver[y][k]=blk[z][k]=0;
        //清除标记
    }
}
int main()
{
    int a;
    for(int i=1;i<=9;i++)
        for(int j=1;j<=9;j++)
        {
            a=max(abs(5-i),abs(5-j));
            mul[i][j]=10-a;
        }//预处理分值
    for(int i=1;i<=9;i++)
    {
        d[i].id=i;//存一下行号,排序后就不会丢了
        for(int j=1;j<=9;j++)
        {
            scanf("%d",&a);
            sum+=mul[i][j]*a;//先更新一波初始分数
            mp[i][j]=a;
            if(!a) {d[i].num++; continue; }//如果是零就跳过
            int x=(i-1)/3,y=(j-1)/3;
            if(hor[i][a]||ver[j][a]||blk[x*3+y+1][a])
            {
                cout<<-1;
                return 0;
            }//判断冲突
            hor[i][a]=1;
            ver[j][a]=1;
            blk[x*3+y+1][a]=1;
            //预处理行列宫的情况
        }
    }
    sort(d+1,d+10,cmp);//按行排一波
    for(int i=1;i<=9;i++)
        for(int j=1;j<=9;j++)
            if(mp[d[i].id][j]==0)
                t[++cnt].x=d[i].id,t[cnt].y=j;//把点按前面排的顺序加进来
    dfs(t[1].x,t[1].y,0,1);//按顺序搜下去
    cout<<ans;
    return 0;
}

 

posted @ 2018-09-09 10:11  LLTYYC  阅读(221)  评论(0编辑  收藏  举报