1067. 精确覆盖问题

题目链接

1067. 精确覆盖问题

给定一个 N×M 的数字矩阵 A,矩阵中的元素 Ai,j{0,1}

请问,你能否在矩阵中找到一个行的集合,使得这些行中,每一列都有且仅有一个数字 1

输入格式

第一行包含两个整数 NM

接下来 N 行,每行包含 M 个整数(01),表示完整的数字矩阵。

输出格式

如果能找到满足条件的行的集合,则在一行中依次输出这些行的编号(行编号 1N)。

如果方案不唯一,则以任意顺序输出任意方案即可。

否则,输出 No Solution!

数据范围

1N,M500,
数据保证矩阵中 1 的数量不超过 5000

输入样例1:

3 3 0 1 0 0 0 1 1 0 0

输出样例1:

1 2 3

输入样例2:

4 4 0 0 0 1 1 0 0 0 1 1 0 1 0 1 0 0

输出样例2:

No Solution!

解题思路

DLX,精确覆盖

DLX 是一种基于十字链表的搜索算法,主要用来解决精确覆盖问题和重复覆盖问题

对于十字链表,即针对稀疏矩阵,即矩阵中 1 的个数不多,对于矩阵中 1 的位置都建立一个节点,每个节点都有上、下、左、右四个循环指针,建立十字链表时一行一行插入,首先插入一行哨兵,后面插入时统一逆插,即将所有矩阵上下和左右翻转,唯一的区别是哨兵行的左右指针没有翻转,这样便区分开了,但要找的是行,对答案没有影响

优化:找到一个含 1 最少的列,然后再该列中枚举选择哪一行,如果选择某一行,则其他行应该删除,因为精确覆盖每一列有且只有一个 1,另外对于选择的行,其中出现 1 的列也应该删除,另外需要注意的一点:删除列时对于真正节点只需要在上下方向删除,因为后面对于某含最少个数的 1 的列,通过上下指针枚举行,而那些要删除的行我们已经在上下方向删除了不会被枚举到,恢复时反向也好恢复

DLX 处理精确覆盖问题甚至有时能处理 O(10000) 的数据
n 为矩阵中 1 的个数,c 为接近 1 的常数,则:

  • 时间复杂度:O(cn)

代码

// Problem: 精确覆盖问题 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/1069/ // Memory Limit: 64 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=5505; int n,m; int L[N],R[N],U[N],D[N],s[N],col[N],row[N],idx; int res[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]++; L[tt]=R[hh]=idx,L[idx]=hh,R[idx]=tt; U[idx]=y,D[idx]=D[y],U[D[y]]=idx,D[y]=idx; 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]) { res[++top]=row[i]; for(int j=L[i];j!=i;j=L[j])remove(col[j]); if(dfs())return true; for(int j=R[i];j!=i;j=R[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 hh=idx,tt=idx; for(int j=1;j<=m;j++) { int x; scanf("%d",&x); if(x)add(hh,tt,i,j); } } if(dfs()) for(int i=1;i<=top;i++)printf("%d ",res[i]); else puts("No Solution!"); return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16905722.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2021-11-19 树的直径
点击右上角即可分享
微信分享提示