P4929 【模板】舞蹈链(DLX)

题目背景

本题是舞蹈链模板——精确覆盖问题

题目描述

给定一个N行M列的矩阵,矩阵中每个元素要么是1,要么是0

你需要在矩阵中挑选出若干行,使得对于矩阵的每一列j,在你挑选的这些行中,有且仅有一行的第j个元素为1

输入输出格式

输入格式:

第一行两个数N,M

接下来N行,每行M个数字0或1,表示这个矩阵

输出格式:

一行输出若干个数表示答案,两个数之间用空格隔开,输出任一可行方案均可,顺序随意

若无解,输出“No Solution!”(不包含引号)

输入输出样例

输入样例#1:
3 3
0 0 1
1 0 0
0 1 0
输出样例#1:
2 1 3
输入样例#2:
3 3
1 0 1
1 1 0
0 1 1
输出样例#2:
No Solution!

说明

N,M≤500

保证矩阵中1的数量不超过5000

代码

舞蹈链板子题,维护矩阵,用双向链表支持删除恢复(回溯)操作

已选集合列点权为1,则删除所有该列点权为1的集合。

 

#include<bits/stdc++.h>
using namespace std;
const int maxn=300000+10;
int l[maxn],r[maxn],u[maxn],d[maxn];//左右上下指针,所在行列
int col[maxn],row[maxn];//所在行列 
int h[maxn];//每行表头 
int s[maxn];//每列点数 
int ans[maxn];
int cnt;
int n,m;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
void init()
{
    for(int i=0;i<=m;i++)
    r[i]=i+1,l[i]=i-1,u[i]=d[i]=i;
    r[m]=0;
    l[0]=m;
    cnt=m;
}
void insert(int R,int C)//行列 
{    
    s[C]++;
    row[++cnt]=R,col[cnt]=C;
    u[cnt]=C,d[cnt]=d[C],u[d[cnt]]=cnt,d[C]=cnt;//双向链表实现 
    if(!h[R])h[R]=r[cnt]=l[cnt]=cnt;
    else r[cnt]=h[R],l[cnt]=l[r[cnt]],r[l[cnt]]=cnt,l[r[cnt]]=cnt; 
}
void remove(int C)
{
    r[l[C]]=r[C],l[r[C]]=l[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])
        u[d[j]]=j,d[u[j]]=j,s[col[j]]++;
    r[l[C]]=C,l[r[C]]=C;
}
void dance(int dep)
{
    if(r[0]==0) 
    {
        for(int i=0;i<dep;i++)printf("%d ",ans[i]);
        exit(0);
    }
    int c=r[0];
    for(int i=r[0];i;i=r[i])if(s[i]<s[c])c=i;//每次选择点数最小一列 
    remove(c);
    for(int i=d[c];i!=c;i=d[i])
    {
        ans[dep]=row[i];
        for(int j=r[i];j!=i;j=r[j])remove(col[j]);
        dance(dep+1);
        for(int j=l[i];j!=i;j=l[j])resume(col[j]);
    }
    resume(c); 
}
int main()
{
    n=read(),m=read();
    init();
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
      {
          int a=read();
          if(a)insert(i,j);
      }
    dance(0);
    printf("No Solution!");
    return 0;
} 
View Code

 

 

 

 

 

 

posted @ 2019-04-26 17:07  DriverBen  阅读(178)  评论(0编辑  收藏  举报