P1074 靶形数独 - dfs带技巧的搜索

P1074 靶形数独

题目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ilZ54xDV-1591084526029)(../图库/1591084204359.png)]

题目理解:

  • 和数独题目类似。行列,小九宫格不能重复。
  • 有加权记分。
  • 中间的分数高。
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-908LK4bo-1591084526037)(../../../../%E5%9D%9A%E6%9E%9C%E5%90%8C%E6%AD%A5%E6%96%87%E4%BB%B6%E5%A4%B9/%E6%88%91%E7%9A%84%E5%9D%9A%E6%9E%9C%E4%BA%91/%E7%AE%97%E6%B3%95/%E5%9B%BE%E5%BA%93/1591084248762.png)]

想法:

  • 加权计分。不用开数组,只用 temp += mp[i]/[j]* min(10 - abs(i - 4), 10 - abs(j - 4));

  • for (int i = 0; i < 9; ++i) {
                for (int j = 0; j < 9; ++j) {
                    temp += mp[i][j] * min(10 - abs(i - 4), 10 - abs(j - 4));
                }
            }
    
  • 超时问题。需要重新优化。不能从0-8行去填数。需要从最好填写的行下手。最好下手就是那行空最少的行。

  • 将空的数量从多到少排列。就找到了填数的行的顺序。这里需要利用结构体来排序。

代码:

//P1074 靶形数独
// Created by majoe on 2020/4/29.
//https://www.luogu.com.cn/problem/P1074
//和数独1784类似

#include <bits/stdc++.h>
using namespace std;

const int maxn = 10;
//cx,cy,cblock记录行,列,小九宫格出现的数字
int mp[maxn][maxn],cx[maxn][maxn],cy[maxn][maxn],cblock[maxn][maxn];
//如果数独无解,就输出-1
int ans=-1;

/**
 * 原解法超时,只有通过优化。优化方法如下:
 * 以前是从第0-8行来填数,现在先填好填的,就是空少的行。
 * 因此需要设置结构体,用于排序。
 */
struct  Node{
    int num, cnt;
}so[9];
//从大到小排列
bool cmp(Node n1 ,Node n2){
    return n1.cnt > n2.cnt;
}
void dfs(int x, int y) {

    //x实际是指在完成第x+1行,但是具体第几行,要看so[sx].num
    int sx = x;

    //具体的行号
    x = so[sx].num;

    //搞定了9行,自然得输出
    if (sx == 9) {
        int temp = 0;
        for (int i = 0; i < 9; ++i) {
            for (int j = 0; j < 9; ++j) {
                temp += mp[i][j] * min(10 - abs(i - 4), 10 - abs(j - 4));
            }
        }
        ans = max(temp,ans);
        return;
    }


        //换行
        if (y == 9) {
            dfs(sx + 1, 0);
            return;
        }


        //如果mp上有数,向右前进,填下一个
        if (mp[x][y]) {
            dfs(sx, y + 1);
        } else { // 如果x,y是0
            for (int i = 1; i <= 9; ++i) {
                //如果占位数组没有被占据
                if (!cx[x][i] && !cy[y][i] && !cblock[x / 3 * 3 + y / 3][i]) {
                    cx[x][i] = 1;
                    cy[y][i] = 1;
                    cblock[x / 3 * 3 + y / 3][i] = 1;
                    mp[x][y] = i;
                    dfs(sx, y + 1);
                    //回溯
                    mp[x][y] = 0;
                    cx[x][i] = 0;
                    cy[y][i] = 0;
                    cblock[x / 3 * 3 + y / 3][i] = 0;
                }
            }
        }
        return;
}


int main() {
        for (int i = 0; i < 9; ++i) {
            so[i].num = i;
            for (int j = 0; j < 9; ++j) {
                cin >> mp[i][j];
                //如果格子中有数,就将占位的数组填上
                if (mp[i][j]) {
                    //so是记录每行上初始的数字个数。
                    so[i].cnt++;
                    cx[i][mp[i][j]] = 1;
                    cy[j][mp[i][j]] = 1;
                    cblock[i / 3 * 3 + j / 3][mp[i][j]] = 1;
                }
            }
        }
        //从最好填的行开始填。其实就是枚举所有的可能
        //让初始化数字多的行,排在前面
        sort(so,so+9,cmp);
        dfs(0, 0);
        cout << ans;
        return 0;

    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值