参考:白书
http://www.cnblogs.com/jh818012/p/3252154.html
http://blog.csdn.net/fp_hzq/article/details/6795027
需要注意的是
(1)行列id的hash
(2)行编号从1开始,列编号为1~c,结点0为表头结点
(3)sz和ans等的初始化操作!!!
精确区间覆盖
16*16的数独问题:LA2659
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int maxnode = 16 * 16 * 16 * 4;
const int maxr = 1024;
const int maxc = 16 * 16 * 16;
///精确区间覆盖
///行编号从1开始,列编号为1~c,结点0为表头结点
///注意给行和列的编号
///注意sz,ans.要自己初始化和赋值!!!
struct DLX
{
int n , sz; // 每列的行数,节点总数
int S[maxc]; // 各列节点总数
int row[maxnode],col[maxnode]; // 各节点行列编号
int L[maxnode],R[maxnode],U[maxnode],D[maxnode]; // 十字链表
int ansd, ans[maxr]; // 解,行数
void init(int n )
{
this->n = n ;
for(int i = 0 ; i <= n; i++ )
{
U[i] = i ;
D[i] = i ;
L[i] = i - 1;
R[i] = i + 1;
}
R[n] = 0 ;
L[0] = n;
sz = n + 1 ;
memset(S,0,sizeof(S));
}
void addRow(int r,vector<int> c1)
{
int first = sz;
for(int i = 0 ; i < c1.size(); i++ )
{
int c = c1[i];
L[sz] = sz - 1 ;
R[sz] = sz + 1 ;
D[sz] = c ;
U[sz] = U[c];
D[U[c]] = sz;
U[c] = sz;
row[sz] = r;
col[sz] = c;
S[c] ++ ;
sz ++ ;
}
R[sz - 1] = first ;
L[first] = sz - 1;
}
// 顺着链表A,遍历除s外的其他元素
#define FOR(i,A,s) for(int i = A[s]; i != s ; i = A[i])
void remove(int c)
{
L[R[c]] = L[c];
R[L[c]] = R[c];
FOR(i,D,c)
FOR(j,R,i)
{
U[D[j]] = U[j];
D[U[j]] = D[j];
--S[col[j]];
}
}
void restore(int c)
{
FOR(i,U,c)
FOR(j,L,i)
{
++S[col[j]];
U[D[j]] = j;
D[U[j]] = j;
}
L[R[c]] = c;
R[L[c]] = c;
}
bool dfs(int d)
{
if(R[0] == 0 )
{
ansd = d;
return true;
}
// 找S最小的列c
int c = R[0], tmp = S[R[0]];
FOR(i, R, 0) if (S[i] < tmp) tmp = S[c = i];
// int c = R[0];
// FOR(i,R,0) if(S[i] < S[c]) c = i;
remove(c);
FOR(i,D,c)
{
ans[d] = row[i];
FOR(j,R,i) remove(col[j]);
if(dfs(d + 1)) return true;
FOR(j,L,i) restore(col[j]);
}
restore(c);
return false;
}
bool solve(vector<int> & v)
{
v.clear();
if(!dfs(0)) return false;
for(int i = 0 ; i< ansd ; i ++ ) v.push_back(ans[i]);
return true;
}
};
DLX solver;
char s[16][17];
int encode(int x, int y, int z)
{
return x * 256 + y * 16 + z + 1;
}
void decode(int val, int &x, int &y, int &z)
{
--val;
z = val % 16; val /= 16;
y = val % 16; val /= 16;
x = val;
}
bool read()
{
for (int i = 0; i < 16; i++)
if (scanf("%s", s[i]) != 1) return false;
return true;
}
int main()
{
int ncase = 1;
while (read())
{
if (ncase != 1) puts("");
ncase++;
solver.init(16 * 16 * 4);
for (int i = 0; i < 16; i++) for (int j = 0; j < 16; j++) for (int c = 0; c < 16; c++)
{
if (s[i][j] == '-' || s[i][j] == 'A' + c)
{
vector<int>v;
v.push_back(encode(0, i, j));
v.push_back(encode(1, i, c));
v.push_back(encode(2, j, c));
v.push_back(encode(3, (i / 4) * 4 + j / 4, c));
solver.addRow(encode(i, j, c), v);
}
}
vector<int>v;
solver.solve(v);
for (int i = 0; i < v.size(); i++)
{
int x, y, z;
decode(v[i], x, y, z);
s[x][y] = 'A' + z;
}
for (int i = 0; i < 16; i++) puts(s[i]);
}
return 0;
}
n皇后问题的应用:只枚举部分列即可(这个题的白书模板超时了???)
DLX SPOJ 1771 Yet Another N-Queen Problem (八皇后变形)
重复区间覆盖:
#include<cstdio>
#include <iostream>
using namespace std;
const int maxnode = 55555;
const int maxr = 16 * 16;
const int maxc = 16 * 16;
int U[maxnode],D[maxnode],L[maxnode],R[maxnode],C[maxnode], row[maxnode];
int H[maxr],S[maxc];
bool v[maxc];
int sz,ans;
///行编号从1开始,列编号为1~c,结点0为表头结点
///注意给行和列的编号
///注意sz,ans.要自己初始化和赋值!!!
void init(int r,int c)///传入行列数
{
for(int i=0; i<=c; ++i)
{
S[i]=0;
D[i]=U[i]=i;
L[i+1]=i;
R[i]=i+1;
}
R[sz = c]=0;
while(r)H[r--]=-1;
}
int geth()//??
{
int ret=0;
for(int c=R[0]; c; c=R[c]) v[c]=1;
for(int c=R[0]; c; c=R[c]) if(v[c])
{
v[c]=0, ++ret;
for(int i=D[c]; i!=c; i=D[i])
for(int j=R[i]; j!=i; j=R[j])v[C[j]]=0;
}
return ret;
}
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=U[c]; i!=c; i=U[i])
R[L[i]]=L[R[i]]=i;
}
void Dance(int d)///第一次调用传入0
{
if(d+geth()>=ans)return;
if(!R[0])
{
ans=d;
return;
}
// int c = R[0];
// for (i = R[0]; i; i = R[i])
// if (S[i] < R[c]) c = i;
int c = R[0], tmp = maxnode;
for (int i = R[R[0]]; i; i = R[i])
if (S[i] < tmp) tmp = S[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);
Dance(d+1);
for(int j=L[i]; j!=i; j=L[j])resume(j);
resume(i);
}
}
void Link(int r,int c)
{
++S[C[++sz]=c];
D[sz]=D[c];
U[D[c]]=sz;
U[sz]=c;
D[c]=sz;
row[sz] = r;
if(H[r]<0)H[r]=L[sz]=R[sz]=sz;
else
{
R[sz]=R[H[r]];
L[R[H[r]]]=sz;
L[sz]=H[r];
R[H[r]]=sz;
}
}
int n, m;
int a[20][20];
int main()
{
int nn, mm;
while(cin >> n >> m)
{
sz = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
scanf("%d", &a[i][j]);
if (a[i][j]) a[i][j] = ++sz;
}
init(n * m, sz);
cin >> nn >> mm;
for (int i = 0; i < n - nn + 1; i++)
for (int j = 0; j < m - mm + 1; j++)
for (int ii = i; ii < i + nn; ii++)
for (int jj = j; jj < j + mm; jj++)
if (a[ii][jj]) Link(i * m + j + 1, a[ii][jj]);
ans = n * m;
Dance(0);
printf("%d\n", ans);
}
return 0;
}
待解决:
http://blog.csdn.net/pi9nc/article/details/11825733(论文中文版)
在n皇后上的应用
每列覆盖特定次数
某些列不需要覆盖