精确覆盖问题 DLX算法 Hihocoder1317,1321

两道入门题
HihoCoder1317
精确覆盖问题:在一个0-1矩阵中,选定部分行,使得每一列都有且只有一个1。求解一种选法。
当然使用搜索来解决,先选择一行,然后将不能选的行给删掉,然后再继续向下,完成后恢复现场。(X算法)
但是这样的方法,在对于矩阵的删除行列和恢复操作太复杂,所以使用一种数据结构--舞蹈链(Dance Link),也就是一个循环十字链表,可以快速的删掉和恢复某行某列。
结合了舞蹈链的搜索就称作DLX算法。
题目链接中有了很不错的分析。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int maxn = 200;
const int maxr = 200;
const int maxnode = 1e6;
struct DLX
{
    int U[maxnode], D[maxnode], L[maxnode], R[maxnode];
    int row[maxnode], col[maxnode];
    int S[maxn], H[maxr], ans[maxr], ansd, sz;
    int n,m;

    void init(int n, int m){
        this->n = n; this->m = m;
        for(int i = 0; i <= m; i++){
            L[i] = i-1; R[i] = i+1; U[i] = D[i] = i;
        }
        L[0] = m; R[m] = 0; sz = m+1;
        memset(S, 0, sizeof(S));
        memset(H, -1, sizeof(H));
    }

    void addNode(int r, int c){
        row[sz] = r; col[sz] = c;
        U[sz] = U[c];  D[sz] = c;
        D[U[sz]] = sz; U[D[sz]] = sz;
        if(H[r] == -1){
            L[sz] = sz; R[sz] = sz;
            H[r] = sz;
        }else{
            R[sz] = H[r]; L[sz] = L[H[r]];
            L[R[sz]] = sz; R[L[sz]] = sz;
        }
        S[col[sz]]++; sz++;
    }
    #define FOR(i, A, s) for(int i = A[s]; i != s; i = A[i])
    void remove(int c){
        R[L[c]] = R[c]; L[R[c]] = L[c];
        FOR(i, D, c) FOR(j, R, i){
            U[D[j]] = U[j]; D[U[j]] = D[j]; S[col[j]]--;
        }
    }
    void restore(int c){
        FOR(i, U, c) FOR(j, L, i){
            U[D[j]] = j; D[U[j]] = j; S[col[j]]++;
        }
        R[L[c]] = c; L[R[c]] = c;
    }
    bool dfs(int d){
        if(R[0] == 0){
            ansd = d;
            return true;
        }
        int c = R[0];
        FOR(i, R, 0){
            if(S[i] < S[c]) c = i;
        }
        remove(c);
        FOR(i, D, c){
            ans[d] = row[i];
            FOR(j, R, i) remove(col[j]);
            if(dfs(d+1)) return true;
            FOR(j, L, i) restore(col[j]);
        }
        restore(c);
        return false;
    }
}dlx;
int t,n,m;
int main()
{
    cin >> t;
    while(t--){
        cin >> n >> m;
        dlx.init(n,m);
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){
                int tmp;
                cin >> tmp;
                if(tmp) dlx.addNode(i,j);
            }
        }
        if(dlx.dfs(0)){
            cout << "Yes\n";
        }else{
            cout << "No\n";
        }
    }
    return 0;
}

HihoCoder1321
解数独问题:
可以转变成精确覆盖问题来解决。
每一行代表这一项决策,共有9*9*9行,i*9*9+j*9+k表示第i行j列的格子里面填k这项决策
每一列表示一个任务,共有4*9*9
有四类任务:

  • SLOT(i,j) 第i行j列要放当前的决策值
  • ROW(i,k) 第i行要有k值
  • COL(j,k) 第j列要有k值
  • SUB(s,k) 第s个九宫要有k值
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int maxn = 1020;
const int maxr = 1020;
const int maxnode = 2e6;
struct DLX
{
    int U[maxnode], D[maxnode], L[maxnode], R[maxnode];
    int row[maxnode], col[maxnode];
    int S[maxn], H[maxr], ans[maxr], ansd, sz;
    int n,m;

    void init(int n, int m){
        this->n = n; this->m = m;
        for(int i = 0; i <= m; i++){
            L[i] = i-1; R[i] = i+1; U[i] = D[i] = i;
        }
        L[0] = m; R[m] = 0; sz = m+1;
        memset(S, 0, sizeof(S));
        memset(H, -1, sizeof(H));
    }

    void addNode(int r, int c){
        row[sz] = r; col[sz] = c;
        U[sz] = U[c];  D[sz] = c;
        D[U[sz]] = sz; U[D[sz]] = sz;
        if(H[r] == -1){
            L[sz] = sz; R[sz] = sz;
            H[r] = sz;
        }else{
            R[sz] = H[r]; L[sz] = L[H[r]];
            L[R[sz]] = sz; R[L[sz]] = sz;
        }
        S[col[sz]]++; sz++;
    }
    #define FOR(i, A, s) for(int i = A[s]; i != s; i = A[i])
    void remove(int c){
        R[L[c]] = R[c]; L[R[c]] = L[c];
        FOR(i, D, c) FOR(j, R, i){
            U[D[j]] = U[j]; D[U[j]] = D[j]; S[col[j]]--;
        }
    }
    void restore(int c){
        FOR(i, U, c) FOR(j, L, i){
            U[D[j]] = j; D[U[j]] = j; S[col[j]]++;
        }
        R[L[c]] = c; L[R[c]] = c;
    }
    bool dfs(int d){
        if(R[0] == 0){
            ansd = d;
            return true;
        }
        int c = R[0];
        FOR(i, R, 0){
            if(S[i] < S[c]) c = i;
        }
        remove(c);
        FOR(i, D, c){
            ans[d] = row[i];
            FOR(j, R, i) remove(col[j]);
            if(dfs(d+1)) return true;
            FOR(j, L, i) restore(col[j]);
        }
        restore(c);
        return false;
    }
    bool solve(vector<int> &v){
        v.clear();
        if(!dfs(0)) return false;
        for(int i = 0; i < ansd; i++){
            v.push_back(ans[i]);
        }
        return true;
    }
}dlx;
int t,n,m;
int cnt_r, cnt_c;
int mp[10][10];
int encode(int x, int y, int z){
    return x*81+y*9+z+1;
}
void decode(int code, int &x, int &y, int &z){
    code--;
    z = code%9; code /= 9;
    y = code%9; code /= 9;
    x = code%9;
}
const int SLOT = 0;
const int ROW = 1;
const int COL = 2;
const int SUB = 3;
int main()
{
    cin >> t;
    cnt_r = 9*9*9; cnt_c = 9*9*4;
    while(t--){
        dlx.init(cnt_r, cnt_c);
        for(int i = 0; i < 9; i++){
            for(int j = 0; j < 9; j++){
                cin >> mp[i][j];
            }
        }
        for(int i = 0; i < 9; i++){
            for(int j = 0; j < 9; j++){
                for(int k = 0; k < 9; k++){
                    if(mp[i][j] == 0 || mp[i][j] == k+1){
                        int r = encode(i, j, k);
                        dlx.addNode(r, encode(SLOT, i, j));
                        dlx.addNode(r, encode(ROW, i, k));
                        dlx.addNode(r, encode(COL, j, k));
                        dlx.addNode(r, encode(SUB, (i/3)*3+j/3, k));// 6,9
                    }
                }
            }
        }
        vector<int> v;
        dlx.solve(v);
        for(int i = 0; i < v.size(); i++){
            int r, c, k;
            decode(v[i], r, c, k);
            mp[r][c] = k+1;
        }
        for(int i = 0; i < 9; i++){
            for(int j = 0; j < 9; j++){
                if(j) cout << ' ';
                cout << mp[i][j];
            }
            cout << "\n";
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/Alruddy/p/7707631.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值