题目背景
本题是舞蹈链模板——精确覆盖问题
题目描述
给定一个N行M列的矩阵,矩阵中每个元素要么是1,要么是0
你需要在矩阵中挑选出若干行,使得对于矩阵的每一列j,在你挑选的这些行中,有且仅有一行的第j个元素为1
输入格式
第一行两个数N,M
接下来N行,每行M个数字0或1,表示这个矩阵
输出格式
一行输出若干个数表示答案,两个数之间用空格隔开,输出任一可行方案均可,顺序随意
若无解,输出“No Solution!”(不包含引号)
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<map>
#include<ctime>
#include<vector>
#define ll long long
#define llu unsigned ll
//#define int ll
using namespace std;
//n*m+m<=250500
const int mr=300100;
const int mc=300100;
const int maxn=300100;
int a[mc];
struct DLX
{
int n,m,si;
int u[maxn],d[maxn],r[maxn],l[maxn],row[maxn],col[maxn];
int h[mr],s[mc];
int ans[mc];
void init(int nn,int mm)
{
n=nn,m=mm;
for(int i=0;i<=m;i++)
{
s[i]=0;
u[i]=d[i]=i;
l[i]=i-1,r[i]=i+1;
}
l[0]=m,r[m]=0;
si=m;
for(int i=1;i<=n;i++)
h[i]=-1;
}
void add(int rr,int cc)
{
si++;
row[si]=rr;
col[si]=cc;
s[cc]++;
d[si]=d[cc];
u[d[cc]]=si;
u[si]=cc;
d[cc]=si;
if(h[rr]<0) h[rr]=l[si]=r[si]=si;
else
{
r[si]=r[h[rr]];
l[r[h[rr]]]=si;
l[si]=h[rr];
r[h[rr]]=si;
}
}
void _remove(int c)
{
l[r[c]]=l[c],r[l[c]]=r[c];
for(int i=d[c];i!=c;i=d[i])
{
for(int j=r[i];j!=i;j=r[j])
{
u[d[j]]=u[j];
d[u[j]]=d[j];
s[col[j]]--;
}
}
}
void _resume(int c)
{
for(int i=u[c];i!=c;i=u[i])
{
for(int j=l[i];j!=i;j=l[j])
{
d[u[j]]=j;
u[d[j]]=j;
s[col[j]]++;
}
}
l[r[c]]=r[l[c]]=c;
}
bool dance(int st)
{
if(r[0]==0)
{
for(int i=0;i<st;i++)
printf("%d ",ans[i]);
return true;
}
int c=r[0];
for(int i=r[0];i!=0;i=r[i])
if(s[i]<s[c]) c=i;
_remove(c);
for(int i=d[c];i!=c;i=d[i])
{
ans[st]=row[i];
for(int j=r[i];j!=i;j=r[j]) _remove(col[j]);
if(dance(st+1)) return true;
for(int j=l[i];j!=i;j=l[j]) _resume(col[j]);
}
_resume(c);
return false;
}
};
DLX sp;
int main(void)
{
int n,m,x;
scanf("%d%d",&n,&m);
sp.init(n,m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&x);
if(x) sp.add(i,j);
}
}
if(!sp.dance(0)) printf("No Solution!\n");
return 0;
}