ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

HUST 1017. Exact cover(Dancing Links存储matrix,dfs)

2022-01-18 18:59:29  阅读:185  来源: 互联网

标签:matrix idx Dancing int tt 元素 Links hh col


Problem
There is an N*M matrix with only 0s and 1s, (1 <= N,M <= 1000). An exact cover is a selection of rows such that every column has a 1 in exactly one of the selected rows.
Try to find out the selected rows.

Input
There are multiply test cases.
First line: two integers N, M;
The following N lines: Every line first comes an integer C(1 <= C <= 100), represents the number of 1s in this row, then comes C integers: the index of the columns whose value is 1 in this row.

Output
First output the number of rows in the selection, then output the index of the selected rows.
If there are multiply selections, you should just output any of them.
If there are no selection, just output “NO”.

Sample Input
6 7
3 1 4 7
2 1 4
3 4 5 7
3 3 5 6
4 2 3 6 7
2 2 7

Sample Output
3 2 4 6

舞蹈链 Dancing Links 介绍

运用十字双向链表(即Dancing Links)这一存储结构存储01稀疏矩阵(元素1的数量较少)中值为1的节点,之后选择1个数最少的列(剪枝),删除(remove)元素最少的那一列,减少搜索次数

从上往下遍历这一列有元素(1)的行,每遍历到某一行则假定选择该行,删除(remove)这一行中有元素(1)的列,递归搜索该方案是否满足条件。由于有可能搜尽了某一分支都没有可行的解,那么则要回溯,注意恢复现场(resume

Dancing Links模板题(AcWing 1067. 精确覆盖问题)代码加详细注释:

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

using namespace std;

const int N = 5510;

int n, m;//矩阵的长宽
int l[N], r[N], u[N], d[N];//每个点上下左右的指针 
int s[N];//每列的节点数 
int row[N], col[N], idx;//每个点所在的行列及点的数量 
int ans[N];//选了哪些集合 相当于一个存答案的栈
int top;//答案栈的栈顶指针

void init()//建立一个空矩阵
{
    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;//0左边是m m右边是0
    idx = m + 1;//一开始空矩阵有m+1个元素 
}

void add(int& hh, int& tt, int x, int y)//在第x行y列插入一个元素(插入到hh,tt之间)
{
    row[idx] = x, col[idx] = y, s[y] ++ ;//当前插入的点编号为idx 记录当前点的行列信息 第y列元素个数增加
    u[idx] = y, d[idx] = d[y];
    u[d[y]] = idx, d[y] = idx;//对于列:这个点是直接插到该列的虚拟节点的后面  构建双向十字链表-列 
    r[hh] = l[tt] = idx;//作为当前该行唯一的节点兼头节点 它的左右指针都指向它自己 
    r[idx] = tt, l[idx] = hh;
    tt = idx ++ ;//tt后移
}

void remove(int p)//删除第p列及该列上有元素的每一行
{
    r[l[p]] = r[p];//该列左边元素指向该列的右指针直接指向该列的右边 相当于直接把该列忽视掉了 
    l[r[p]] = l[p];//该列右边元素同理 直接指向该列左侧 
    for (int i = d[p]; i != p; i = d[i])//遍历该列上的元素
        for (int j = r[i]; j != i; j = r[j])//遍历这个元素所在的这一行上的元素 
        {
            s[col[j]] -- ;//这个点所在的列元素数量减1 
            u[d[j]] = u[j];//注意我们处理的点是该列有1的行内的点
            d[u[j]] = d[j];//将我们要处理的点在这个点的纵向删除掉 
        }
}//不难发现 删除的本质分为两步:单独处理掉本列; 在横向以点为单位逐个删除掉 

void resume(int p)//恢复本列及相关元素 
{
    for (int i = u[p]; i != p; i = u[i])//遍历相关行 
        for (int j = l[i]; j != i; j = l[j])//遍历行上的点 
        {
            u[d[j]] = j;//将该点与上下节点的关系恢复回来 
            d[u[j]] = j;
            s[col[j]] ++ ;//将该点所在的列上的元素个数加回来 
        }
    //删除与恢复的对称
    r[l[p]] = p;
    l[r[p]] = p;
}

bool dfs()
{
    if (!r[0]) return true;//矩阵空了 搜索成功 
    int p = r[0];
    for (int i = r[0]; i; i = r[i])//从左到右遍历列 
        if (s[i] < s[p])//找到元素1个数最少的那一列
            p = i;
    remove(p);//删除元素最少的那一列 减少搜索次数 
    for (int i = d[p]; i != p; i = d[i])//从上往下遍历p列有元素的行 
    {
        ans[ ++ top] = row[i];//假定选择该行 
        for (int j = r[i]; j != i; j = r[j]) remove(col[j]);//删除这一行中有元素的列 
        if (dfs()) return true;//递归搜索该方案是否满足条件 
        for (int j = l[i]; j != i; j = l[j]) resume(col[j]);//复原
        top -- ;
    }
    resume(p);//不要忘记同时要恢复p本身这一列 
    return false;
}

int main()
{
    scanf("%d%d", &n, &m);
    init();
    for (int i = 1; i <= n; i ++ )
    {
        int hh = idx, tt = idx;
        for (int j = 1; j <= m; j ++ )
        {
            int x;
            scanf("%d", &x);
            if (x==1) add(hh, tt, i, j);//将需要的元素添加到链表中 
        }
    }

    if (dfs())
    {
        for (int i = 1; i <= top; i ++ ) printf("%d ", ans[i]);//输出选中了哪几行 
        puts("");
    }
    else puts("No Solution!");

    return 0;
}

将上方的模板稍作扩展可得本题(Exact cover)代码如下:

#include<bits/stdc++.h>

using namespace std;

const int N = 5510;

int n, m;
int l[N], r[N], u[N], d[N], s[N], row[N], col[N], idx;
int ans[N], top;

void init()
{
    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;
    idx = m + 1;
}

void add(int& hh, int& tt, int x, int y)
{
    row[idx] = x, col[idx] = y, s[y] ++ ;
    u[idx] = y, d[idx] = d[y], u[d[y]] = idx, d[y] = idx;
    r[hh] = l[tt] = idx, r[idx] = tt, l[idx] = hh;
    tt = idx ++ ;
}

void remove(int p)
{
    r[l[p]] = r[p], l[r[p]] = l[p];
    for (int i = d[p]; i != p; i = d[i])
        for (int j = r[i]; j != i; j = r[j])
        {
            s[col[j]] -- ;
            u[d[j]] = u[j], d[u[j]] = d[j];
        }
}

void resume(int p)
{
    for (int i = u[p]; i != p; i = u[i])
        for (int j = l[i]; j != i; j = l[j])
        {
            u[d[j]] = j, d[u[j]] = j;
            s[col[j]] ++ ;
        }
    r[l[p]] = p, l[r[p]] = p;
}

bool dfs()
{
    if (!r[0]) return true;
    int p = r[0];
    for (int i = r[0]; i; i = r[i])
        if (s[i] < s[p])
            p = i;
    remove(p);
    for (int i = d[p]; i != p; i = d[i])
    {
        ans[ ++ top] = row[i];
        for (int j = r[i]; j != i; j = r[j]) remove(col[j]);
        if (dfs()) return true;
        for (int j = l[i]; j != i; j = l[j]) resume(col[j]);
        top -- ;
    }
    resume(p);
    return false;
}

int main()
{
    scanf("%d%d", &n, &m);
    init();
    for(int i=1;i<=n;++i)
    {
    	int c;
    	cin>>c;
    	vector<int> a(c+1);
    	for(int j=1;j<=c;++j) cin>>a[j];
    	int p = 1;
    	int hh = idx,tt = idx;
    	for(int k=1;k<=m;++k)
    	{
    		if(k==a[p]) add(hh,tt,i,k),p++;
		}
	}

    if (dfs())
    {
    	cout<<top<<' ';
        for (int i = 1; i < top; i ++ ) printf("%d ", ans[i]);
        cout<<ans[top]<<endl;
        cout<<endl;
    }
    else cout<<"NO"<<endl;

    return 0;
}

标签:matrix,idx,Dancing,int,tt,元素,Links,hh,col
来源: https://blog.csdn.net/Jacob0824/article/details/122565773

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有