dancing links (hust 1017)

题目链接:http://acm.hust.edu.cn/problem/show/1017

裸的舞蹈链模版题

关于舞蹈链算法请参考:http://www.cnblogs.com/grenet/p/3145800.html
这个博客讲解的很清楚详细

舞蹈链算法实际上是一个双向十字交叉链表数据结构,解决精确覆盖问题,数独等等都很方便。花了近一天终于搞懂额,纪念一下a的第一道dancing links 啦啦啦~

#include <iostream>
#include <cstdio>
#include<algorithm>
#include <cstring>

#define M_node 1000000
#define M_col 1005

using namespace std;

int n,m; // n 行 m 列
int up[M_node],down[M_node],lleft[M_node],rright[M_node],col[M_node],row[M_node],colnum[M_col],ans[M_col],h[M_col]; // colnum 每列元素数量,ans 结果, h 行头节点

void init()
{
    memset(colnum, 0, sizeof(colnum));
    memset(ans, 0, sizeof(ans));
    memset(h, -1, sizeof(h));
    for (int i = 0; i <= m; i ++) // 初始化head及列标节点
    {
        up[i] = i;
        down[i] = i;
        if(i == 0) lleft[i] = m;
        else  lleft[i] = i - 1;
        if(i == m) rright[i] = 0;
        else  rright[i] = i + 1;
        col[i] = i;
        row[i] = 0;
    }

}

void addNode(int r,int c,int id) // 在双向交叉链表中添加元素,id 节点编号
{
    row[id] = r; col[id] = c; // 记录行,列
    up[id] = up[c]; down[up[c]] = id; // 连接上下
    down[id] = c; up[c] = id;
    if (h[r] == -1)
    {
        h[r] = id;
        rright[id] = id; lleft[id] = id;
    }
    else
    {
        lleft[id] = lleft[h[r]]; rright[lleft[h[r]]] = id;
        rright[id] = h[r]; lleft[h[r]] = id;
    }
    colnum[c] ++;
}

void remove_c(int c) // 删除一列
{
    rright[lleft[c]] = rright[c];
    lleft[rright[c]] = lleft[c];
    for (int i = down[c]; i != c; i = down[i])
    {
        for (int j = rright[i]; j != i; j = rright[j])
        {
            down[up[j]] = down[j];
            up[down[j]] = up[j];
            colnum[col[j]] --;
        }
    }
}

void resume_c(int c) // 恢复一列
{
    rright[lleft[c]] = c;
    lleft[rright[c]] = c;
    for (int i = up[c]; i != c; i = up[i])
    {
        for (int j = lleft[i]; j != i; j = lleft[j])
        {
            down[up[j]] = j;
            up[down[j]] = j;
            colnum[col[j]] ++;
        }
    }

}

bool dance(int k) // k 表示进行到第几层
{
    if (!lleft[0]) // 列表元素全部为空
    {
        sort(ans, ans + k);
        cout << k << " ";
        for (int i = 0;i < k - 1; i ++) cout << ans[i] << " ";
        cout << ans[k - 1] << endl;
        return true;
    }

    int s_col = rright[0];  // 记录选中列
    for (int i = rright[0]; i != 0; i = rright[i])   // 选择元素最少的一列,减少递归次序
    {
        if (colnum[s_col] > colnum[i])
        {
            s_col = i;
        }
    }
    remove_c(s_col);

    for (int i = down[s_col]; i != s_col; i = down[i]) // 任意选择该列一行
    {
        ans[k] = row[i]; // 记录选中行
        for (int j = rright[i]; j != i; j = rright[j]) // 删除该行相关列
        {
            remove_c(col[j]);
        }

        if (dance(k + 1))  return true; // 继续操作

        for (int j = lleft[i]; j != i; j = lleft[j]) // 恢复该行相关列
        {
            resume_c(col[j]);
        }
    }
    resume_c(s_col);
    return false;
}

int main()
{
    while (cin >> n >> m)
    {
        init();   // 建表初始化
        int c,t_col,nodeNum = m + 1;
        for (int i = 1; i <= n; i ++)  // i 行
        {
            cin >> c;
            for(int j = 0; j < c; j ++)
            {
                cin >> t_col;   // 列
                addNode(i, t_col, nodeNum);
                nodeNum ++;
            }
        }
        if (!dance(0)) cout << "NO" << endl;

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值