版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39562952/article/details/83177463
精确覆盖模板:(解决的问题是在01矩阵中选出最少的行,另每一列都有且只有一个1)
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
const int MN = 1005;//最多的行数
const int MM = 1005;//最多的列数
const int MNN = 1e5 + 5 + MM; //最大节点数
struct DLX
{
int n, m, si;//行数,列数,目前使用的节点数
int U[MNN], D[MNN], L[MNN], R[MNN], Row[MNN], Col[MNN];
//第i个结点的U向上指针D下L左R右,所在位置Row行Col列
int row[MN], col[MM];//记录行的选择情况和列的覆盖情况
int ansd, ans[MN];//答案的总数,答案
void init(int n, int m)//初始化空表
{
for (int i = 0; i <= m; i++)
{
col[i] = 0;//说明i列没1覆盖
U[i] = D[i] = i;//目前的纵向是空的,所以上下都指着自己
L[i] = i - 1;
R[i] = i + 1;//横向连接
}
L[0] = m;
R[m] = 0;//第0行首尾相连
si = m;//目前用了前0-m个节点
for (int i = 1; i <= n; i++)
row[i] = -1;//i行没被选择过
}
void add(int r, int c)//插入节点,节点位置在(r,c)
{
si++;
Col[si] = c;//该节点在c列
col[c]++;//c列多了一个1
Row[si] = r;//该节点在r行
D[si] = D[c], U[D[c]] = si;
U[si] = c, D[c] = si;//c是列首,si是列尾,所以u列尾=列首
if (row[r] < 0)
row[r] = L[si] = R[si] = si;
else
{
R[si] = R[row[r]];//si的右边=行首的右边
L[R[row[r]]] = si;//行首的右边的左边变成si
L[si] = row[r];//si的左边=行首
R[row[r]] = si;//行首的右边的=si
}
}
void remove(int c)//删除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];
col[Col[j]]--;//那列的元素个数减一
}
}
}
void resume(int c)//恢复c列
{
L[R[c]] = c;//把c回复到0行
R[L[c]] = c;
for (int i = U[c]; i != c; i = U[i])
{
for (int j = R[i]; j != i; j = R[j])
{
U[D[j]] = j;
D[U[j]] = j;
col[Col[j]]++;
}
}
}
int dance(int d)//选取了一共d行
{
if (R[0] == 0)//全覆盖
{
ansd = d;
return 1;
}
int c = R[0];
for (int i = R[0]; i != 0; i = R[i])//删除最少元素的列
{
if (col[i] < col[c])
{
c = i;
}
}
remove(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]);//删除j对应的列
}
if (dance(d + 1))
return 1;
for (int j = L[i]; j != i; j = L[j])
{
resume(Col[j]);//恢复j对应的列
}
}
resume(c);//恢复c列
return 0;
}
}dlx;
int main()
{
return 0;
}
重复覆盖模板:(解决的问题是在01矩阵中选出最少的行,另每一列都至少有一个1)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int NUM = 100 * 60;
struct SLX
{
int U[NUM], D[NUM], L[NUM], R[NUM];//上下左右的邻居
int Col[NUM], Row[NUM];//每个元素对应的列行
int col[NUM], row[NUM];//每一列的元素个数,行的行首
int si;
int ans[1000], ansd;
void ini(int n, int m)
{
for (int i = 0; i <= m; i++)
{
R[i] = i + 1;
L[i] = i - 1;
D[i] = U[i] = i;
Col[i] = i;
Row[i] = 0;
col[i] = 0;
}
R[m] = 0;
L[0] = m;
si = m;
for (int i = 1; i <= n; i++)
{
row[i] = -1;
}
}
void add(int r, int c)
{
si++;
Col[si] = c;
Row[si] = r;
col[c]++;
D[si] = D[c];
U[D[c]] = si;
U[si] = c;
D[c] = si;
if (row[r] < 0)
{
row[r] = R[si] = L[si] = si;
}
else
{
R[si] = R[row[r]];
L[R[row[r]]] = si;
L[si] = row[r];
R[row[r]] = si;
}
}
void remove(int c)
{
for (int i = D[c]; i != c; i = D[i])
{
R[L[i]] = R[i];
L[R[i]] = L[i];
}
}
void resume(int c)
{
for (int i = D[c]; i != c; i = D[i])
{
R[L[i]] = L[R[i]] = i;
}
}
int geth()//最少还要处理几列
{
int ret = 0;
bool vis[80];
memset(vis, 0, sizeof(vis));
for (int c = R[0]; c; c = R[c])
{
if (!vis[c])
{
ret++;
for (int i = D[c]; i != c; i = D[i])
{
for (int j = R[i]; j != i; j = R[j])
{
vis[Col[j]] = true;
}
}
}
}
return ret;
}
void dance(int k)
{
if (!R[0])
{
ansd = min(ansd, k);
return;
}
else if(k+geth()>=ansd)
{
return;
}
int c = R[0];
for (int i = R[0]; i; i = R[i])
{
if (col[i] < col[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);
col[Col[j]]--;
}
dance(k + 1);
for (int j = L[i]; j != i; j = L[j])
resume(j),col[Col[j]]++;
resume(i);
}
}
}dlx;
int main()
{
return 0;
}