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

      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天,期间感冒……

     

你可能感兴趣的:(c,优化,insert,UP)