DLX (精确区间覆盖,重复区间覆盖)(模板)

8 篇文章 0 订阅
4 篇文章 0 订阅

参考:白书

http://www.cnblogs.com/jh818012/p/3252154.html

http://blog.csdn.net/fp_hzq/article/details/6795027

需要注意的是

(1)行列id的hash

(2)行编号从1开始,列编号为1~c,结点0为表头结点

(3)sz和ans等的初始化操作!!!


精确区间覆盖

16*16的数独问题:LA2659

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

const int maxnode = 16 * 16 * 16 * 4;
const int maxr = 1024;
const int maxc = 16 * 16 * 16;

///精确区间覆盖
///行编号从1开始,列编号为1~c,结点0为表头结点
///注意给行和列的编号
///注意sz,ans.要自己初始化和赋值!!!
struct DLX
{
    int n , sz;                                                 // 每列的行数,节点总数
    int S[maxc];                                                // 各列节点总数
    int row[maxnode],col[maxnode];                              // 各节点行列编号
    int L[maxnode],R[maxnode],U[maxnode],D[maxnode];            // 十字链表

    int ansd, ans[maxr];                                         // 解,行数

    void init(int n )
    {
        this->n = n ;
        for(int i = 0 ; i <= n; i++ )
        {
            U[i] = i ;
            D[i] = i ;
            L[i] = i - 1;
            R[i] = i + 1;
        }
        R[n] = 0 ;
        L[0] = n;
        sz = n + 1 ;
        memset(S,0,sizeof(S));
    }
    void addRow(int r,vector<int> c1)
    {
        int first = sz;
        for(int i = 0 ; i < c1.size(); i++ )
        {
            int c = c1[i];
            L[sz] = sz - 1 ;
            R[sz] = sz + 1 ;
            D[sz] = c ;
            U[sz] = U[c];
            D[U[c]] = sz;
            U[c] = sz;
            row[sz] = r;
            col[sz] = c;
            S[c] ++ ;
            sz ++ ;
        }
        R[sz - 1] = first ;
        L[first] = sz - 1;
    }

    // 顺着链表A,遍历除s外的其他元素
#define FOR(i,A,s) for(int i = A[s]; i != s ; i = A[i])

    void remove(int c)
    {
        L[R[c]] = L[c];
        R[L[c]] = R[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)
        {
            ++S[col[j]];
            U[D[j]] = j;
            D[U[j]] = j;
        }
        L[R[c]] = c;
        R[L[c]] = c;
    }
    bool dfs(int d)
    {
        if(R[0] == 0 )
        {
            ansd = d;
            return true;
        }
        // 找S最小的列c
        int c = R[0], tmp = S[R[0]];
        FOR(i, R, 0) if (S[i] < tmp) tmp = S[c = i];
//        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 solver;
char s[16][17];

int encode(int x, int y, int z)
{
    return x * 256 + y * 16 + z + 1;
}
void decode(int val, int &x, int &y, int &z)
{
    --val;
    z = val % 16;  val /= 16;
    y = val % 16; val /= 16;
    x = val;
}

bool read()
{
    for (int i = 0; i < 16; i++)
        if (scanf("%s", s[i]) != 1) return false;
    return true;
}
int main()
{
    int ncase = 1;
    while (read())
    {
        if (ncase != 1) puts("");
        ncase++;
        solver.init(16 * 16 * 4);
        for (int i = 0; i < 16; i++) for (int j = 0; j < 16; j++) for (int c = 0; c < 16; c++)
        {
            if (s[i][j] == '-' || s[i][j] == 'A' + c)
            {
                vector<int>v;
                v.push_back(encode(0, i, j));
                v.push_back(encode(1, i, c));
                v.push_back(encode(2, j, c));
                v.push_back(encode(3, (i / 4) * 4 + j / 4, c));
                solver.addRow(encode(i, j, c), v);
            }
        }
        vector<int>v;
        solver.solve(v);

        for (int i = 0; i < v.size(); i++)
        {
            int x, y, z;
            decode(v[i], x, y, z);
            s[x][y] = 'A' + z;
        }

        for (int i = 0; i < 16; i++) puts(s[i]);
    }
    return 0;
}


n皇后问题的应用:只枚举部分列即可(这个题的白书模板超时了???)

 

DLX SPOJ 1771 Yet Another N-Queen Problem (八皇后变形)



重复区间覆盖:

Problem 1686 神龙的难题

#include<cstdio>
#include <iostream>
using namespace std;

const int maxnode = 55555;
const int maxr = 16 * 16;
const int maxc = 16 * 16;

int U[maxnode],D[maxnode],L[maxnode],R[maxnode],C[maxnode], row[maxnode];
int H[maxr],S[maxc];
bool v[maxc];
int sz,ans;
///行编号从1开始,列编号为1~c,结点0为表头结点
///注意给行和列的编号
///注意sz,ans.要自己初始化和赋值!!!
void init(int r,int c)///传入行列数
{
    for(int i=0; i<=c; ++i)
    {
        S[i]=0;
        D[i]=U[i]=i;
        L[i+1]=i;
        R[i]=i+1;
    }
    R[sz = c]=0;
    while(r)H[r--]=-1;
}
int geth()//??
{
    int ret=0;
    for(int c=R[0]; c; c=R[c]) v[c]=1;
    for(int c=R[0]; c; c=R[c]) if(v[c])
    {
        v[c]=0, ++ret;
        for(int i=D[c]; i!=c; i=D[i])
            for(int j=R[i]; j!=i; j=R[j])v[C[j]]=0;
    }
    return ret;
}
void remove(int c)
{
    for(int i=D[c]; i!=c; i=D[i])
        R[L[i]]=R[i],L[R[i]]=L[i];
}
void resume(int c)
{
    for(int i=U[c]; i!=c; i=U[i])
        R[L[i]]=L[R[i]]=i;
}
void Dance(int d)///第一次调用传入0
{
    if(d+geth()>=ans)return;
    if(!R[0])
    {
        ans=d;
        return;
    }

//    int c = R[0];
//    for (i = R[0]; i; i = R[i])
//        if (S[i] < R[c]) c = i;
    int c = R[0], tmp = maxnode;
    for (int i = R[R[0]]; i; i = R[i])
        if (S[i] < tmp) tmp = S[c = i];

    for(int i=D[c]; i!=c; i=D[i])
    {
        remove(i);
        for(int j=R[i]; j!=i; j=R[j])remove(j);
        Dance(d+1);
        for(int j=L[i]; j!=i; j=L[j])resume(j);
        resume(i);
    }
}
void Link(int r,int c)
{
    ++S[C[++sz]=c];
    D[sz]=D[c];
    U[D[c]]=sz;
    U[sz]=c;
    D[c]=sz;
    row[sz] = r;
    if(H[r]<0)H[r]=L[sz]=R[sz]=sz;
    else
    {
        R[sz]=R[H[r]];
        L[R[H[r]]]=sz;
        L[sz]=H[r];
        R[H[r]]=sz;
    }
}

int n, m;
int a[20][20];
int main()
{
    int nn, mm;
    while(cin >> n >> m)
    {
        sz = 0;
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
            {
                scanf("%d", &a[i][j]);
                if (a[i][j]) a[i][j] = ++sz;
            }
        init(n * m, sz);
        cin >> nn >> mm;
        for (int i = 0; i < n - nn + 1; i++)
            for (int j = 0; j < m - mm + 1; j++)
                for (int ii = i; ii < i + nn; ii++)
                    for (int jj = j; jj < j + mm; jj++)
                        if (a[ii][jj]) Link(i * m + j + 1, a[ii][jj]);
        ans = n * m;
        Dance(0);
        printf("%d\n", ans);
    }
    return 0;
}


待解决:

http://blog.csdn.net/pi9nc/article/details/11825733(论文中文版)

在n皇后上的应用

每列覆盖特定次数

某些列不需要覆盖


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值