您的位置:首页 > 其它

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

2016-08-13 10:28 246 查看
做了几道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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  acm