您的位置:首页 > 其它

Dancing Links + A* 应用于精确覆盖、重复覆盖

2011-10-18 20:39 267 查看
Dancing Links是由Knuth提出的用于一类搜索问题的通用优化。

或称DLX。

主要应用于精确覆盖和重复覆盖。

精确覆盖题目:

POJ3740、POJ3074、POJ3076、HDU4069

重复覆盖题目:

HDU3529、HDU2295、POJ1084

关于DLX的详细介绍可以去查阅相关资料。

假设一个0-1矩阵,要求选择某几个行,使得所有的列均只有一个1(精确覆盖),或者至少有一个1(重复覆盖)。

DLX使用双向链表极大地优化了搜索。

DLX可以说是一种模板。应用于一类题目。只要把问题建模转化为一定的格式,则可以应用DLX。

精确覆盖应用于解数独、八皇后等。据说目前解数独速度最快的就是DLX。

重复覆盖应用于类似雷达覆盖的问题。

精确覆盖模板:

void remove(const int &c)
{
l[r[c]] = l[c];
r[l[c]] = r[c];
int i, j;
for (i=d[c]; i!=c; i=d[i])
{
for (j=r[i]; j!=i; j=r[j])
{
u[d[j]] = u[j];
d[u[j]] = d[j];
s[ch[j]]--;
}
}
}

void resume(const int &c)
{
int i, j;
for (i=u[c]; i!=c; i=u[i])
{
for (j=l[i]; j!=i; j=l[j])
{
s[ch[j]]++;
u[d[j]] = j;
d[u[j]] = j;
}
}
l[r[c]] = c;
r[l[c]] = c;
}

bool dfs(const int &k)
{
if (r[head]==head)
{
return true;
}
int ss = INT_MAX;
int c;
int i, j;
for (i=r[head]; i!=head; i=r[i])
{
if (s[i]<ss)
{
ss = s[i];
c = i;
}
}
remove(c);
for (i=d[c]; i!=c; i=d[i])
{
o[k] = rh[i];
len = k;
for (j=r[i]; j!=i; j=r[j]) remove(ch[j]);
if (dfs(k+1)) return true;
for (j=l[i]; j!=i; j=l[j]) resume(ch[j]);
}
resume(c);
return false;
}


重复覆盖模板:

void remove(int c)
{
int i;
for (i=d[c]; i!=c; i=d[i])
{
l[r[i]] = l[i];
r[l[i]] = r[i];
}
}

void resume(int c)
{
int i;
for (i=u[c]; i!=c; i=u[i])
{
l[r[i]] = r[l[i]] = i;
}
}

int h()
{
memset(used, false, sizeof(used));
int c, i, j;
int ret = 0;
for (c=r[head]; c!=head; c=r[c])
{
if (!used[c])
{
ret++;
used[c] = true;
for (i=d[c]; i!=c; i=d[i])
{
for (j=r[i]; j!=i; j=r[j])
{
used[ch[j]] = true;
}
}
}
}
return ret;
}

void dfs(int k)
{
if (k+h()>=len)//启发式搜索
return;
if (r[head]==head)
{
len = k;
return;
}
int ss = INT_MAX;
int c, i, j;
for (i=r[head]; i!=head; i=r[i])
{
if (s[i]<ss)
{
ss = s[i];
c = i;
}
}
for (i=d[c]; i!=c; i=d[i])
{
remove(i);
for (j=r[i]; j!=i; j=r[j]) remove(j);
dfs(k+1);
for (j=l[i]; j!=i; j=l[j]) resume(j);
resume(i);
}
}

共用部分:

int new_node(int up, int down, int left, int right)
{
l[size] = left;
r[size] = right;
u[size] = up;
d[size] = down;
l[right] = r[left] = u[down] = d[up] = size;
return size++;
}

void init(int n, int m)
{
size = 0;
head = new_node(0, 0, 0, 0);
len = n;
int i;
for (i=1; i<=m; i++)
{
new_node(i, i, l[head], head);
ch[i] = i;
s[i] = 0;
}
for (i=0; i<=n; i++)
rh[i] = -1;
}

void insert_node(int i, int j)
{
ch[size] = j;
s[j]++;
if (rh[i]==-1)
{
rh[i] = new_node(j, d[j], size, size);
}
else
{
new_node(j, d[j], rh[i], r[rh[i]]);
}
}

DLX一般难在建模。只要建好模,一切都好办。

不过有些题目建模过程极其猥琐……比如POJ1084……

题解:

POJ3740:精确覆盖的基本题目。

POJ3074、POJ3076:数独的基本题目。把数组转化为01矩阵再进行DLX。

HDU4069:数独小变种,只是块的部分改变了,影响不大。

HDU3529:以空格为行,障碍为列,进行重复覆盖即可。

HDU2295:NC的我,由于一个字母打错,TLE了10次,然后精度问题WA了3次,还有一个CE,最终AC……二分雷达的半径进行重复覆盖,以雷达为行,以城市为列。

POJ1084:超恶心的一道题目。重复覆盖。以火柴为行,以方格为列。若某火柴支配某个方格(可多个方格),则标记为1。题目恶心在,方格的边长必须从小到大排列;如果从大到小排列的话,超时没话说。做这道题耗时n天,期间感冒……
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: