2713. 重复覆盖问题

题目链接

2713. 重复覆盖问题

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

请你在矩阵中找到一个行的集合,使得这些行中,每一列都包含数字 1,并且集合中包含的行数尽可能少。

输入格式

第一行包含两个整数 NM

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

输出格式

输出共两行。

第一行输出集合最少需要包含的行数。

第二行输出集合中包含的行的编号(行编号 1N)。

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

数据范围

1N,M100,
本题数据随机生成,
数据保证有解且答案不超过 20

输入样例:

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

输出样例:

2 1 2

解题思路

DLX,重复覆盖,IDEA*

DLX 的重复覆盖问题的主题框架:IDEA,即迭代加深+估价函数,同精确覆盖,建立十字链表后首先选择未被选中的含 1 最少的列,然后在列中选择行,由于该行已经被选中了,则该行中所有含有 1 的列也应该删除,即对于行中所有含 1 的那一列中所有在左右方向的节点都要删除,注意该行中在左右方向的节点不能删除,一方面这些节点压根不会被遍历到,另一方面还需要恢复,最后关键的地方在于估计函数的设计:找出所有没被覆盖的列,对于某一列来说,其至少要从里面选择一行,但我们可以选择将所有的行全部选中,但是代价却只有一行的代价,最后的总代价肯定是不超过真实代价的,即如果当前选择的函数加上估计函数要大于枚举的答案的话肯定不行

  • 时间复杂度:O()

代码

// Problem: 重复覆盖问题 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/2715/ // 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=10105; int n,m; int L[N],R[N],U[N],D[N],idx,row[N],col[N],s[N]; bool st[105]; int res[N]; 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]++; R[hh]=L[tt]=idx,R[idx]=tt,L[idx]=hh; U[idx]=y,D[idx]=D[y],U[D[y]]=idx,D[y]=idx; tt=idx++; } int h() { int res=0; memset(st,0,sizeof st); for(int i=R[0];i;i=R[i]) { if(st[i])continue; res++; st[i]=true; for(int j=D[i];j!=i;j=D[j]) for(int k=R[j];k!=j;k=R[k])st[col[k]]=true; } return res; } void remove(int p) { for(int i=D[p];i!=p;i=D[i]) L[R[i]]=L[i],R[L[i]]=R[i]; } void resume(int p) { for(int i=U[p];i!=p;i=U[i]) L[R[i]]=i,R[L[i]]=i; } bool dfs(int k,int dep) { if(k+h()>dep)return false; 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; for(int i=D[p];i!=p;i=D[i]) { res[k]=row[i]; remove(i); for(int j=R[i];j!=i;j=R[j])remove(j); if(dfs(k+1,dep))return true; for(int j=L[i];j!=i;j=L[j])resume(j); resume(i); } 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); } } int depth=0; while(!dfs(0,depth))depth++; printf("%d\n",depth); for(int i=0;i<depth;i++)printf("%d ",res[i]); return 0; }

__EOF__

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