DLX 舞蹈链 精确覆盖+可重复覆盖

做了几道DLX 感觉这种板子很像网络流的使用

就是建矩阵

DLX大致有两种 精确覆盖+可重复覆盖

选择行使得所有的列里面都存在1的变量,前者是唯一的,后者是不唯一的,很简单

所以两个板子(窝也只能用板子,,,,,套啊套,在做题中理解板子

两个板子能达到的功能是   记录选择的行,限制行选择的个数,判断是否能覆盖(这当然是最基础的

两个不同的地方也就是remove 和 resume 两个函数

限制行数的函数在第二个板子里面有

先是精确覆盖

struct DLX
{
    int n,m,size;
    int U[maxn],D[maxn],R[maxn],L[maxn],Row[maxn],Col[maxn];
    int H[maxn],S[maxn];//n,m的范围
    int ansd;
    void init(int _n,int _m){
        n = _n;
        m = _m;
        for(int i=0;i<=m;i++){
            S[i] = 0;
            U[i] = D[i] = i;//双向十字链表
            L[i] = i-1;
            R[i] = i+1;
        }
        R[m] = 0,L[0] = m;
        size = m;
        size = m;
        for(int i=1;i<=n;i++)H[i] = -1;
    }
    void Link(int r,int c){
        ++S[Col[++size]=c];
        Row[size] = r;
        D[size] = D[c];
        U[D[c]] = size;
        U[size] = c;
        D[c] = size;
        if(H[r] < 0)H[r] = L[size] = R[size] = size;
        else{
            R[size] = R[H[r]];
            L[R[H[r]]] = size;
            L[size] = H[r];
            R[H[r]] = size;
        }
    }
    //这里的remove其实并没有真正删除掉结点,可以用resume恢复
    void remove(int c)  //删除第c列上的元素所在行 
    {
        L[R[c]] = L[c]; R[L[c]] = R[c];
        //删除c,c是列指针,删除了c就代表删除了整列,因为递归后不可能访问到c了 
        for(int i = D[c];i != c;i = D[i]) //c所在列上的元素i 
            for(int j = R[i];j != i;j = R[j]) //i和j一行的,删掉j 
            {
                U[D[j]] = U[j];
                D[U[j]] = D[j];
                --S[Col[j]]; //j所在列的元素(‘1’的个数)-1;
            }
    }
    void resume(int c)  //对应的恢复操作 
    {
        for(int i = U[c];i != c;i = U[i])
            for(int j = L[i];j != i;j = L[j])
                ++S[Col[U[D[j]]=D[U[j]]=j]];
        L[R[c]] = R[L[c]] = c;
    }
    void Dance(int d)
    {
         //剪枝下
        if(ansd != -1 && ansd <= d)return;
        if(R[0] == 0)
        {
            if(ansd == -1)ansd = d;
            else if(d < ansd)ansd = d;
            return;
        }
        int c = R[0];
        for(int i = R[0];i != 0;i = R[i]) if(S[i] < S[c]) c = i;  //找元素最少的列c,一种优化 
        remove(c); //删除列c 
        for(int i = D[c];i != c;i = D[i])
        {
            for(int j = R[i];j != i;j = R[j])remove(Col[j]);  //删除所有可能的冲突元素  
             Dance(d+1);
            for(int j = L[i];j != i;j = L[j])resume(Col[j]);
        }
        resume(c);
    }
};
/*ans.init() ans.Link(x,y) ans.ansd = -1 ans.Dance(0) tmp.ansd > 0 ? */
    int n,m;
    DLX tmp;
    while(cin >> n >> m){
        tmp.init(n,m);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                int a;cin >> a;
                if(a)tmp.Link(i,j);//存入1
            }
        }
        tmp.ansd = -1;//答案
        tmp.Dance(0);
        if(tmp.ansd > 0);//满足情况
        else ;//不能满足情况
    }













可重复覆盖

//限制行数,可重复覆盖
struct DLX
{
    int L[maxnode],R[maxnode],U[maxnode],D[maxnode];
    int Row[maxnode],Col[maxnode];
    int S[maxn],H[maxn];
    int ansd;
    int n,m,size;
    void init(int _n,int _m){
        n = _n,m = _m;
        for(int i=0;i<=m;i++){
            S[i] = 0;
            U[i] = D[i] = i;
            L[i] = i-1;
            R[i] = i+1;
        }
        R[m] = 0;
        L[0] = m;
        size = m;
        for(int i=1;i<=n;i++)H[i] = -1;
    }
    void Link(int r,int c){
        ++S[Col[++size]=c];
        Row[size] = r;
        D[size] = D[c];
        U[D[c]] = size;
        U[size] = c;
        D[c] = size;
        if(H[r] < 0)H[r] = L[size] = R[size] = size;
        else{
            R[size] = R[H[r]];
            L[R[H[r]]] = size;
            L[size] = H[r];
            R[H[r]] = size;
        }
    }
    void remove(int c){
        for(int i=D[c];i!=c;i=D[i]){
            L[R[i]] = L[i],R[L[i]] = R[i];
        }
    }
    void resume(int c){
        for(int i=U[c];i!=c;i=U[i]){
            L[R[i]] = R[L[i]] = i;
        }
    }
    bool v[1000];//限制选择的行数
    int f()//计算选择的行数
    {
        int ret = 0;
        for(int c=R[0];c!=0;c=R[c])v[c] = 1;
        for(int c=R[0];c!=0;c=R[c]){
            if(v[c]){
                ret++;
                v[c] = 0;
                for(int i=D[c];i!=c;i=D[i]){
                    for(int j=R[i];j!=i;j=R[j]){
                        v[Col[j]]=0;
                    }
                }
            }
        }
        return ret;
    }
    bool Dance(int d){
        if(d+f()>k)return 0;//选择不能大于k行
        if(R[0] == 0)return d <= k;
        int c = R[0];
        for(int i=R[0];i!=0;i=R[i])
            if(S[i] < S[c])c = i;
        for(int i=D[c];i!=c;i=D[i]){
            remove(i);
            for(int j=R[i];j!=i;j=R[j])remove(j);
            if(Dance(d+1))return 1;
            for(int j=L[i];j!=i;j=L[j])resume(j);
            resume(i);
        }
        return 0;
    }
};
DLX ans;ans.init(n,n);ans.ansd = -1;
ans.Link(i,j);
ans.Dance(0);



下面是几个板子题

精确覆盖:

poj 3074 板子一套就没有


hust 1071 还是板子题,用来练手,把选择到的行数输出就ok了,很简单,板子里面都有,在dance里面的ans数组存着


hdu 3663 从这个题就可以看出来DLX的使用和网络流很相似就是建模型

(最后选择的行是乱序的哦,,,

这个题我们可以将所有的可能被取区间当作的行,然后列为每个站的所有1-d的所有天数,为了每个站的值取一个区间我们,在列里面添加一行,这一行都置为1,是的同个站的区间只取一次,其实就是很简单的建模,有木有?

代码贴上来

/*  ^^ ====== ^^ 
ID: meixiuxiu
PROG: test
LANG: C++11
*/
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <sstream>
#include <cctype>
#include <bitset>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int ,int> pii;
#define MEM(a,b) memset(a,b,sizeof a)
#define CLR(a) memset(a,0,sizeof a);
#define pi acos(-1.0)
#define maxnode 360005
#define maxn 1005
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
std::vector<int> edge[100];
int ans[maxnode];
int U[maxnode],D[maxnode],R[maxnode],L[maxnode];
int to[10005];
int Row[maxnode],Col[maxnode];//元素对应的行列
struct DLX
{
    int H[maxn],S[maxn];//s记录该列残余的1个数,h该行最左端的1
    int ansd;
    int n,m,size;
    void init(int _n,int _m){
        n = _n;
        m = _m;
        for(int i=0;i<=m;i++){
            S[i] = 0;
            U[i] = D[i] = i;//双向十字链表
            L[i] = i-1;
            R[i] = i+1;
        }
        R[m] = 0,L[0] = m;
        size = m;
        for(int i=1;i<=n;i++)H[i] = -1;
    }
    void Link(int r,int c){
        ++S[Col[++size]=c];
        Row[size] = r;
        D[size] = D[c];
        U[D[c]] = size;
        U[size] = c;
        D[c] = size;
        if(H[r] < 0)H[r] = L[size] = R[size] = size;
        else{
            R[size] = R[H[r]];
            L[R[H[r]]] = size;
            L[size] = H[r];
            R[H[r]] = size;
        }
    }
    //这里的remove其实并没有真正删除掉结点,可以用resume恢复
    void remove(int c)  //删除第c列上的元素所在行 
    {
        L[R[c]] = L[c]; R[L[c]] = R[c];
        //删除c,c是列指针,删除了c就代表删除了整列,因为递归后不可能访问到c了 
        for(int i = D[c];i != c;i = D[i]) //c所在列上的元素i 
            for(int j = R[i];j != i;j = R[j]) //i和j一行的,删掉j 
            {
                U[D[j]] = U[j];
                D[U[j]] = D[j];
                --S[Col[j]]; //j所在列的元素(‘1’的个数)-1;
            }
    }
    void resume(int c)  //对应的恢复操作 
    {
        for(int i = U[c];i != c;i = U[i])
            for(int j = L[i];j != i;j = L[j])
                ++S[Col[U[D[j]]=D[U[j]]=j]];
        L[R[c]] = R[L[c]] = c;
    }
    int Dance(int d)
    {
         //剪枝下
        if(ansd != -1 && ansd <= d)return 1;
        if(R[0] == 0)
        {
            if(ansd == -1)ansd = d;
            else if(d < ansd)ansd = d;
            return 1;
        }
        int c = R[0];
        for(int i = R[0];i != 0;i = R[i]) if(S[i] < S[c]) c = i;  //找元素最少的列c,一种优化 
        remove(c); //删除列c 
        for(int i = D[c];i != c;i = D[i])
        {
            ans[d] = Row[i];//记录最后选择的行
            for(int j = R[i];j != i;j = R[j])remove(Col[j]);  //删除所有可能的冲突元素  
            if(Dance(d+1))return 1;
            for(int j = L[i];j != i;j = L[j])resume(Col[j]);
        }
        resume(c);
        return 0;
    }
};
int vis[66][66];
int main()
{
#ifdef LOCAL
	freopen("in.txt", "r", stdin);
//	freopen("out.txt","w",stdout);
#endif
    int n,m,d;
    while(cin >> n >> m >> d){
        DLX tmp;
        MEM(vis,0);
        for(int i=1;i<=n;i++)edge[i].clear();
        for(int i=1;i<=m;i++){
            int u,v;scanf("%d%d",&u,&v);
            if(vis[u][v])continue;
            vis[u][v] = vis[v][u] = 1;
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        int _n = 0, _m = 0;
        int s[100],e[100];
        int mp[1000][2];
        for(int i=1;i<=n;i++){
            scanf("%d%d",&s[i],&e[i]);
            edge[i].push_back(i);
            _n += e[i]-s[i]+1 + 1 + (e[i]-s[i]+1)*(e[i]-s[i])/2;
        }
        _m = n*d+n;
        tmp.init(_n,_m);
        int row = 0;
        int xx = 0;
        for(int i=1;i<=n;i++){
            tmp.Link(++row,n*d+i);
            to[row] = i;
            mp[row][0] = mp[row][1] = 0;
            for(int j=s[i];j<=e[i];j++){
                for(int k=j;k<=e[i];k++){
                    ++row;
                    mp[row][0] = j;
                    mp[row][1] = k;
                    to[row] = i;
                    for(int t=0;t<edge[i].size();t++){
                        int v = edge[i][t];
                        for(int p = j;p <= k;p++){
                            tmp.Link(row,(v-1)*d+p);
                        }
                    }
                    tmp.Link(row,n*d+i);
                    xx = max(xx,n*d+i);
                }
            }
        }
        tmp.ansd = -1;
        if(tmp.Dance(0)){
            int res[105][2];
            for(int i=0;i<tmp.ansd;i++){
                //printf("%d %d\n",mp[ans[i]][0],mp[ans[i]][1]);
                //cout << to[ans[i]] << endl;
                res[to[ans[i]]][0] = mp[ans[i]][0];
                res[to[ans[i]]][1] = mp[ans[i]][1];
            }
            for(int i=1;i<=n;i++){
                printf("%d %d\n",res[i][0],res[i][1]);
            }
        }
        else {
            printf("No solution\n");
        }
        puts("");
    }
	return 0;
}

可重复覆盖

hdu 5046:

DLX的板子可以实现限制行数的选择去找,那么就很简单了,二分+套板子

二分我们可以将距离离散化一下,这样可以快点

代码入下

/*  ^^ ====== ^^ 
ID: meixiuxiu
PROG: test
LANG: C++11
*/
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <sstream>
#include <cctype>
#include <bitset>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int ,int> pii;
#define MEM(a,b) memset(a,b,sizeof a)
#define CLR(a) memset(a,0,sizeof a);
#define pi acos(-1.0)
#define maxnode 100*100
#define maxn 105
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int k;
struct DLX
{
    int L[maxnode],R[maxnode],U[maxnode],D[maxnode];
    int Row[maxnode],Col[maxnode];
    int S[maxn],H[maxn];
    int ansd;
    int n,m,size;
    void init(int _n,int _m){
        n = _n,m = _m;
        for(int i=0;i<=m;i++){
            S[i] = 0;
            U[i] = D[i] = i;
            L[i] = i-1;
            R[i] = i+1;
        }
        R[m] = 0;
        L[0] = m;
        size = m;
        for(int i=1;i<=n;i++)H[i] = -1;
    }
    void Link(int r,int c){
        ++S[Col[++size]=c];
        Row[size] = r;
        D[size] = D[c];
        U[D[c]] = size;
        U[size] = c;
        D[c] = size;
        if(H[r] < 0)H[r] = L[size] = R[size] = size;
        else{
            R[size] = R[H[r]];
            L[R[H[r]]] = size;
            L[size] = H[r];
            R[H[r]] = size;
        }
    }
    void remove(int c){
        for(int i=D[c];i!=c;i=D[i]){
            L[R[i]] = L[i],R[L[i]] = R[i];
        }
    }
    void resume(int c){
        for(int i=U[c];i!=c;i=U[i]){
            L[R[i]] = R[L[i]] = i;
        }
    }
    bool v[1000];//限制选择的行数
    int f()//计算选择的行数
    {
        int ret = 0;
        for(int c=R[0];c!=0;c=R[c])v[c] = 1;
        for(int c=R[0];c!=0;c=R[c]){
            if(v[c]){
                ret++;
                v[c] = 0;
                for(int i=D[c];i!=c;i=D[i]){
                    for(int j=R[i];j!=i;j=R[j]){
                        v[Col[j]]=0;
                    }
                }
            }
        }
        return ret;
    }
    bool Dance(int d){
        if(d+f()>k)return 0;//选择不能大于k行
        if(R[0] == 0)return d <= k;
        int c = R[0];
        for(int i=R[0];i!=0;i=R[i])
            if(S[i] < S[c])c = i;
        for(int i=D[c];i!=c;i=D[i]){
            remove(i);
            for(int j=R[i];j!=i;j=R[j])remove(j);
            if(Dance(d+1))return 1;
            for(int j=L[i];j!=i;j=L[j])resume(j);
            resume(i);
        }
        return 0;
    }
};
int x[105],y[105],n;
bool check(ll limit){
    DLX ans;ans.init(n,n);ans.ansd = -1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(1ll*fabs(x[i]-x[j])+1ll*fabs(y[i]-y[j])<=limit){
                ans.Link(i,j);
            }
        }
    }
    if(ans.Dance(0))return 1;
    return 0;
}
int main()
{
#ifdef LOCAL
	freopen("in.txt", "r", stdin);
//	freopen("out.txt","w",stdout);
#endif
    int t;cin >> t;
    int kase = 1;
    while(t--){
        cin >> n >> k;
        for(int i=1;i<=n;i++)cin >> x[i] >> y[i];
        ll tmp[10005];int c = 0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                tmp[++c] = 1ll*fabs(x[i]-x[j])+1ll*fabs(y[i]-y[j]);
            }
        }
        sort(tmp+1,tmp+1+c);
        int cnt = unique(tmp+1,tmp+1+c)-(tmp+1);
        int l = 1,r = cnt;
        while(l < r){
            int mid = (l+r)>>1;
            //cout << tmp[mid] << endl;
            if(check(tmp[mid]))r = mid;
            else l = mid+1;
        }
        printf("Case #%d: %lld\n",kase++,tmp[l]);
    }
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值