wxyz-wing

本文深入探讨了数独解决技巧中的WXYZ翼(WXYZ Wing)方法,详细介绍了如何通过该技巧确定可消除的候选数字,以及如何在数独盘面上查找符合WXYZ翼条件的位置。通过具体的实现代码,帮助读者理解这一高级技巧的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

// [11/17/2010 anning]
// WXYZ-Wing
bool Sudoku::WXYZ_Wing(int n)
{
    struct sdWXYZ
    {
        _Point  p;
        int     candi;
    };

    // W -
    // X -
    // Y -
    // Z- X, Y, W 这样可以确定共同的数字为 Z
    // 每个候选数,对应的双数位置,前4个为row和box的组合,后4个为列和box的组合
    sdWXYZ wing[8][3];
    int num[8];
    _Point arrDelPt[2];     // 最多可删除候选数的位置个数
    _Point arrHouse[20];    // WXYZ位置可以看到的所有数格

    for (int i = 0; i < 81; ++i)
    {
        _Point pt (i);
        ANSudokuCell &cell = m_anCell[pt.r][pt.c];
        // WXYZ 数格要求是4个候选数
        if (cell.n != n) continue;

        // 确定是否可以组成WXYZ,及哪个数字为Z
        memset(num, 0, sizeof(num));
        int idx;
        int k;
       
        bool bValEqual = false;
        int nTotal = GetInterSection(pt, pt, 20, arrHouse);
        for (int lH = 0; lH < nTotal; ++lH)
        {
            _Point & p = arrHouse[lH];
            ANSudokuCell &c2 = m_anCell[p.r][p.c];
            if (c2.n != 2) continue; // WZ, XZ, YZ都是双数
            if (!IsInclude(cell, c2)) continue;

            if (pt.r == p.r || pt.BoxNo()==p.BoxNo()) // row or box
            {
                idx = GetIndex(cell, c2.candi[0]);
                for (k = 0; k < num[idx]; ++k) {
                    if (wing[idx][k].candi == c2.candi[1]) {
                        bValEqual = true;
                        break;
                    }
                }
                if (bValEqual) break;
                wing[idx][num[idx]].candi = c2.candi[1];
                wing[idx][num[idx]].p = p;
                ++num[idx];

                idx = GetIndex(cell, c2.candi[1]);
                for (k = 0; k < num[idx]; ++k) {
                    if (wing[idx][k].candi == c2.candi[0]) {
                        bValEqual = true;
                        break;
                    }
                }
                if (bValEqual) break;
                wing[idx][num[idx]].candi = c2.candi[0];
                wing[idx][num[idx]].p = p;
                ++num[idx];
            }
           
            if (pt.c == p.c || pt.BoxNo()==p.BoxNo()) // col or box
            {
                idx = GetIndex(cell, c2.candi[0])+4;
                for (k = 0; k < num[idx]; ++k) {
                    if (wing[idx][k].candi == c2.candi[1]) {
                        bValEqual = true;
                        break;
                    }
                }
                if (bValEqual) break;
                wing[idx][num[idx]].candi = c2.candi[1];
                wing[idx][num[idx]].p = p;
                ++num[idx];
               
                idx = GetIndex(cell, c2.candi[1])+4;
                for (k = 0; k < num[idx]; ++k) {
                    if (wing[idx][k].candi == c2.candi[0]) {
                        bValEqual = true;
                        break;
                    }
                }
                if (bValEqual) break;
                wing[idx][num[idx]].candi = c2.candi[0];
                wing[idx][num[idx]].p = p;
                ++num[idx];
            }
        }
        // 有相等的双值数格,为数对 Naked pair ,故查找下一个WXYZ位置
        if (bValEqual) continue;
       
        // row/row & box能否组成WXYZ-Wing
        int j;
        for (int m = 0; m < 8; ++m)
        {
            if (num[m] != n-1) continue;
           
            k = 0;
            // 满足WXYZ-Wing,再看是否有可消的候选数
            if (m < 4)
            {
                int _colBegin = (pt.c/3) * 3;
                int _colEnd = _colBegin + 3;
                for (int cc = _colBegin; cc < _colEnd; ++cc)
                {
                    if (cc == pt.c) // 过滤 WXYZ 所在位置
                        continue;
                   
                    ANSudokuCell & cDel = m_anCell[pt.r][cc];
                    for (j = 0; j < cDel.n; ++j)
                    {
                        if (cDel.candi[j] == cell.candi[m])
                        {
                            arrDelPt[k].r = pt.r;
                            arrDelPt[k].c = cc;
                            ++k;
                            break;
                        }
                    }
                }
            }
            else
            {
                int _rowBegin = (pt.r/3) * 3;
                int _rowEnd = _rowBegin + 3;
                for (int rr = _rowBegin; rr < _rowEnd; ++rr)
                {
                    if (rr == pt.r) // 过滤 WXYZ 所在位置
                        continue;
                   
                    ANSudokuCell & cDel = m_anCell[rr][pt.c];
                    for (j = 0; j < cDel.n; ++j)
                    {
                        if (cDel.candi[j] == cell.candi[m-4])
                        {
                            arrDelPt[k].r = rr;
                            arrDelPt[k].c = pt.c;
                            ++k;
                            break;
                        }
                    }
                }
            }
            if (k <= 0) continue;
           
            /*
            printf("WXYZ-Wing(n=%d) (%d,%d)  Z:%d", n,
                pt.r, pt.c,
                k < 4 ? cell.candi[m] : cell.candi[m-4] );
           
            for (j = 0; j < num[m]; ++j)
            {
                printf("(%d,%d)",wing[m][j].p.r, wing[m][j].p.c);
            }
           
            printf("Cells to del:");
            for (j = 0; j < k; ++j)
            {
                printf("(%d,%d) ", arrDelPt[j].r, arrDelPt[j].c);
            }
            printf("/n");
            */
        }
    }
   
    return false;
}

// 从c的candi[]中取出相等n的值的索引(0~c.n-1),无则返回-100
int GetIndex(ANSudokuCell & c, int n)
{
    for (int i = 0; i < c.n; ++i)
    {
        if (c.candi[i] == n)
            return i;
    }

    return -100; // error
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值