「舞蹈链 DLX 」学习笔记

· · 题解

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int Maxn=5000+500+10;
int n,m,l[Maxn],r[Maxn],u[Maxn],d[Maxn];// 十字链表左右上下
int idx,ansn,ans[Maxn],s[Maxn],row[Maxn],col[Maxn];
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 &hd,int &tl,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[hd]=l[tl]=idx;r[idx]=tl;l[idx]=hd;
    tl=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 resum(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 1;
    int p=r[0];
    for(int i=r[0];i;i=r[i])
        if(s[i]<s[p])p=i; // 优先选择 1 最多的行
    remove(p);
    for(int i=d[p];i!=p;i=d[i])
    {   
        ans[++ansn]=row[i];
        for(int j=r[i];j!=i;j=r[j])remove(col[j]);
        if(dfs())return 1;
        for(int j=l[i];j!=i;j=l[j])resum(col[j]);
        ansn--;
    }
    resum(p);
    return 0;
}
int main()
{   
    scanf("%d%d",&n,&m);
    init();
    for(int i=1;i<=n;i++)
    {   
        int hd=idx,tl=idx;
        for(int j=1;j<=m;j++)
        {   
            int x;
            scanf("%d",&x);
            if(x)add(hd,tl,i,j);
        }
    }
    if(dfs())
        for(int i=1;i<=ansn;i++)
            printf("%d ",ans[i]);
    else printf("No Solution!");
    printf("\n");
    return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int Maxn=10000+5;
int n,m,l[Maxn],r[Maxn],u[Maxn],d[Maxn],s[Maxn];
int idx,tot,ans[Maxn],col[Maxn],row[Maxn];
bool st[Maxn];
void init()
{   
    for(int i=0;i<=m;i++)
    {   
        l[i]=i-1;r[i]=i+1;
        u[i]=d[i]=col[i]=i;
        s[i]=0;
    }
    l[0]=m;r[m]=0;
    idx=m+1;
}
int h() //估价函数 
{   
    int cnt=0;
    memset(st,0,sizeof(st));
    for(int i=r[0];i;i=r[i])
    {   
        if(st[col[i]])continue;
        cnt++;
        st[col[i]]=1;
        for(int j=d[i];j!=i;j=d[j])
            for(int k=r[j];k!=j;k=r[k])
                st[col[k]]=1;
    }
    return cnt;
} 
void add(int &hd,int &tl,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[hd]=l[tl]=idx;r[idx]=tl;l[idx]=hd;
    tl=idx++;
}
void remove(int p) // 注意列并不会删除 
{   
    for(int i=d[p];i!=p;i=d[i])
        r[l[i]]=r[i],l[r[i]]=l[i];
}
void resum(int p) // 同上 
{   
    for(int i=u[p];i!=p;i=u[i])
        r[l[i]]=i,l[r[i]]=i;
}
bool dfs(int k)
{   
    if(k+h()-1>tot)return 0;
    if(!r[0])return 1;
    int p=r[0];
    for(int i=r[0];i;i=r[i])
        if(s[p]>s[i])p=i;
    // 注意选完列,p 所在的列并不会删除。 
    for(int i=d[p];i!=p;i=d[i])
    {   
        ans[k]=row[i];
        remove(i);
        for(int j=r[i];j!=i;j=r[j])remove(j);
        if(dfs(k+1))return 1;
        for(int j=l[i];j!=i;j=l[j])resum(j);
        resum(i);
    }
    return 0;
}
int main()
{   
    scanf("%d%d",&n,&m);
    init();
    for(int i=1;i<=n;i++)
    {   
        int hd=idx,tl=idx;
        for(int j=1;j<=m;j++)
        {   
            int x;
            scanf("%d",&x);
            if(x)add(hd,tl,i,j);
        }
    }
    tot=0;
    while(!dfs(1))tot++;
    printf("%d\n",tot);
    for(int i=1;i<=tot;i++)
        printf("%d ",ans[i]);
    return 0;
}
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int Maxn=20000;
struct node{
    int x,y;
    char c;
}op[Maxn];
int m=16*16*4,l[Maxn],r[Maxn],u[Maxn],d[Maxn];// 十字链表左右上下
int idx,ansn,ans[Maxn],s[Maxn],row[Maxn],col[Maxn];
char c[20][20];
void init() // 预处理 
{   
    for(int i=0;i<=m;i++)
    {   
        l[i]=i-1;r[i]=i+1;s[i]=0;
        u[i]=d[i]=i;
    }
    l[0]=m;r[m]=0;
    idx=m+1;
}
void add(int &hd,int &tl,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[hd]=l[tl]=idx;r[idx]=tl;l[idx]=hd;
    tl=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 resum(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 1;
    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[++ansn]=row[i];
        for(int j=r[i];j!=i;j=r[j])remove(col[j]);
        if(dfs())return 1;
        for(int j=l[i];j!=i;j=l[j])resum(col[j]);
        ansn--;
    }
    resum(p);
    return 0;
}
void w()
{   
    memset(r,0,sizeof(r));memset(u,0,sizeof(u));
    memset(l,0,sizeof(l));memset(u,0,sizeof(u));
    memset(s,0,sizeof(s));
    memset(col,0,sizeof(col));
    memset(row,0,sizeof(row));
}
int main()
{   int T,cases=0;
    scanf("%d",&T);
    while(T--)
    {   
        for(int i=0;i<16;i++)
            scanf("%s",c[i]);
        if(cases)printf("\n");
        cases++;
        w();
        init();
        for(int i=0,n=1;i<16;i++)
            for(int j=0;j<16;j++)
            {   
                int a=0,b=15;
                if(c[i][j]!='-')a=b=c[i][j]-'A';
                for(int k=a;k<=b;k++,n++)
                {   
                    int hd=idx,tl=idx;
                    op[n].x=i;op[n].y=j;op[n].c=k+'A';
                    add(hd,tl,n,i*16+j+1);
                    add(hd,tl,n,256+i*16+k+1);
                    add(hd,tl,n,256*2+j*16+k+1);
                    add(hd,tl,n,256*3+(i/4*4+j/4)*16+k+1);
                }
            }
        dfs();
        for(int i=1;i<=ansn;i++)
            c[op[ans[i]].x][op[ans[i]].y]=op[ans[i]].c;
        for(int i=0;i<16;i++)
            printf("%s\n",c[i]);
    }
    return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector> 
#include<algorithm>
using namespace std;
const int Maxn=3600+5;
int T,n,m,l[Maxn],r[Maxn],u[Maxn],d[Maxn],s[Maxn];
int idx,tot,ans[Maxn],col[Maxn],row[Maxn];
bool st[Maxn]; // 1.作为估价函数 2.作为建图 
vector<int> sq[60];
void init()
{   
    for(int i=0;i<=m;i++)
    {   
        l[i]=i-1;r[i]=i+1;
        u[i]=d[i]=col[i]=i;
        s[i]=0;
    }
    l[0]=m;r[m]=0;
    idx=m+1;
}
int h() //估价函数 
{   
    int cnt=0;
    memset(st,0,sizeof(st));
    for(int i=r[0];i;i=r[i])
    {   
        if(st[col[i]])continue;
        cnt++;
        st[col[i]]=1;
        for(int j=d[i];j!=i;j=d[j])
            for(int k=r[j];k!=j;k=r[k])
                st[col[k]]=1;
    }
    return cnt;
} 
void add(int &hd,int &tl,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[hd]=l[tl]=idx;r[idx]=tl;l[idx]=hd;
    tl=idx++;
}
void remove(int p)
{   
    for(int i=d[p];i!=p;i=d[i])
        r[l[i]]=r[i],l[r[i]]=l[i];
}
void resum(int p)
{   
    for(int i=u[p];i!=p;i=u[i])
        r[l[i]]=i,l[r[i]]=i;
}
bool dfs(int k)
{   
    if(k+h()-1>tot)return 0;
    if(!r[0])return 1;
    int p=r[0];
    for(int i=r[0];i;i=r[i])
        if(s[p]>s[i])p=i;
    for(int i=d[p];i!=p;i=d[i])
    {   
        ans[k]=row[i];
        remove(i);
        for(int j=r[i];j!=i;j=r[j])remove(j);
        if(dfs(k+1))return 1;
        for(int j=l[i];j!=i;j=l[j])resum(j);
        resum(i);
    }
    return 0;
}
int main()
{   
    scanf("%d",&T);
    while(T--)
    {   
        int k;
        scanf("%d%d",&n,&k);
        m=idx=0;
        memset(st,0,sizeof(st));
        memset(col,0,sizeof(col));
        for(int i=1;i<=k;i++)
        {   
            int x;
            scanf("%d",&x);
            st[x]=1;
        }
        for(int len=1;len<=n;len++)
            for(int x=1;x+len-1<=n;x++)
                for(int y=1;y+len-1<=n;y++)
                {
                    m++;
                    sq[m].clear();
                    int dd=n*2+1;//一行过去(横n加上竖n+1)个数
                    for(int i=0;i<len;i++) //向一个矩形的 4 边(上下左右)连去 
                    {   
                        sq[m].push_back((x-1)*dd+y+i); //上 
                        sq[m].push_back((x-1)*dd+y+i+dd*len); //下 
                        sq[m].push_back((x-1)*dd+y+n+i*dd); //左 
                        sq[m].push_back((x-1)*dd+y+n+i*dd+len); //右 
                    }
                    for(int i=0;i<sq[m].size();i++) //相连的边有一个断了,直接删除所有即可 
                        if(st[sq[m][i]]){m--;break;}
                }
        init();
        for(int i=1;i<=n*(n+1)*2;i++) //全部边
            if(!st[i])
            {   
                int hh=idx,tl=idx;
                for(int j=1;j<=m;j++)
                    if(find(sq[j].begin(),sq[j].end(),i)!=sq[j].end()) //连边存在 
                        add(hh,tl,i,j);
            } 
        tot=0;
        while(!dfs(1))tot++;
        printf("%d\n",tot);
    }
    return 0;
}
\text{by Rainy7}