dancing link 资料
来源:互联网 发布:c语言九九乘法编程 编辑:程序博客网 时间:2024/06/06 05:38
http://blog.csdn.net/mu399/article/details/7627862 问题转化为 精确覆盖的方法
http://www.cnblogs.com/grenet/p/3145800.html dancing link 详细说明,VB实现
DancingLinks的应用
把dancingLink应用于实际问题时,只有一个难点,就是如何把具体的问题转换为可以精确覆盖的01矩阵模型,一旦完成了这个步后,直接套用模板就可以解决问题了。
应用之一:伤脑筋十二块
伤脑筋十二块是dancing links精确覆盖的典型应用,理解起来最容易
图2,12片5格骨牌的拼图
题目描述:
给你12个如上图的5格骨牌,如何让程序拼出如上的“口”字图形。
上图是题目的一个答案,你知道程序如何得到这个答案的吗?没错,就是用dancinglink的精确覆盖
我们想象一个有72列的矩阵,其中12列是12个骨牌,剩下60列是60个非中心部分的格子,构造出
所有可能的行来代表在一块骨牌在棋盘上得放置方案;每行有一些‘’1“,用来标识被覆盖的格子,5个1标识一个骨牌放置的位置(恰有1568个这样的行)
我们将最前面的12列命名为F,I,L,P,N,T,U,V,W,X,Y,Z,并且我们可以用两个数字i,j给矩阵中对应棋盘上的第i行第j列格子的那一列命名。通过给出那些出现了‘’1"的列的名字,可以很方便地表示每一行。
例如,图2就是与下面12行的对应的精确覆盖。
1568个行指的是12个骨牌可放置方案的总和,比如长条骨牌I共有64种放置方案,1568中就包含了这64种
这1568行中,每行都有6个1,分布在72个列中
这个矩阵的构造思路是:
首先找约束关系,这里只有两个约束关系,
(1)12个骨牌,每种只有1个,
(2)60个空格中,一个位置只能放一种骨牌(否则就要重叠着放了)
因为第一个约束关系,有了12个列来区分骨牌种类,因为第二个约束关系,有了60个选5个来表示骨牌放置
应用之二:数独问题 sudoku
解数独,生成数独,都可以使用精确覆盖,要把数独问题构造成01矩阵还是有一定的难度
首先找约束关系,这里只有四个约束关系,
(1)81个格子中每个格子只能放一个数字
(2)每一行的数字不能重复
(3)每一列的数字不能重复
(4)每一九宫内的数字不能重复
四个约束关系中,每个约束对应一个列域,对于第二个约束关系,数独中共有9行,每行可以填9个不同的数字
因此第二个列域,共有9* 9,81个列,依此类推,数独问题共有列324个。
由于81个格子,每个格子都最多有9种选择,所以行最多有81*9=729行
这样01矩阵的每行都有4个1,第一个1分布在1到81列,第二个1分布在82到162列,第三个1分布在163到243列,
最后一个1分布在其余列区域。
思考:为什么不能这样构造01矩阵,用5个1,第一个1表示格子序号,有81个列,第二个1表示数字,从1到9有9个列,第三个1表示行号,有9行,第四个1表示列号也有9个,第五个1表示九宫格序号,也有9个,这样共有117列。
为了便于理解,举个例子
9,2,0,0,0,0,0,0,0,
5,0,0,8,7,0,0,0,0,
0,3,8,0,9,1,0,0,0,
0,5,2,9,3,0,1,6,0,
0,9,0,0,0,0,0,3,0,
0,7,3,0,6,4,9,8,0,
0,0,0,4,1,0,2,5,0,
0,0,0,0,5,3,0,0,1,
0,0,0,0,0,0,0,7,3
如上数独有空格40个,已知格子41个,把这个数独构造成01矩阵,矩阵的行有
40*9+41 共401行
对于第一个数字9,在1到81列的第一列,在82到162列的第9个,即90列,在163列到243列的第9个,在244到324列的第9个各占一个1
对于第三个数字0,由于有9个选择,所以在构造01矩阵时,要向矩阵插入9个行,来表示各种可能
对于数独的生成
总体思路是一行一行的生成,第一行可以用一个随机的1到9的排列,接下来的8行,每行都要用dancinglink求解可行的排序
(1)先对1到9这9个数进行随机排列,把这个排列作为数独终盘布局的第一行
(2)自己写函数筛选出下一行,每个格子可以填写的数字集合,筛选时不用考虑行冲突
比如对于排列5,9,7,4,2,6,8,3,1
筛选结果如下: 123468,123468,123468,135789,135789,135789,245679,245679,245679
表示对于下一行的1,2,3列,可以选择的数字集合有1,2,3,4,6,8.
下一行的4,5,6列,可以选择的数字集合有1,3,5,7,8,9
下一行的7,8,9列,可以选择的数字集合有2,4,5,6,7,9
这时,构造01矩阵,就只有2个约束关系
1 对于下一行的9个格子,每个格子只能放一个数字
2 对于下一行的9个格子中的数字,每个数字都不能重复
因为第3个和4个约束,已经在筛选时考虑进去,这里不需再多此一举
这时的01矩阵,列有9+ 9=18个,行有6* 9 = 54行(6+6+6+6+6+6+6+6+6)。
应用之三:N皇后
N皇后问题也可以转换为dancinglinks的精确覆盖问题
这里只讲如何把n皇后问题转换为01矩阵,首先有四个约束关系
(1)所有皇后不能在同一行
(2)所有皇后不能在同一列
(3)所有皇后不能在同一左斜线
(4)所有皇后不能在同一右斜线
为了便于理解,举个例子
n=8时,有8行,8列,15个左斜线,15个右斜线(2*n-1)
这样构造的矩阵有46个列,8*8=64个行
矩阵的每行都有4个1,分别分布在行域,列域,左斜线域,右斜线域
在编程求解这个问题时,需要做一点变通,因为左斜线域,右斜线域的列不可能被全部覆盖
因此只需行域和列域被完全覆盖就算找到问题的一个解了。
附:
dancing LInks 求解数独的C++代码
- #include<iostream>
- #include<string.h>
- using namespace std;
- struct Node
- {
- Node *up;
- Node *down;
- Node *left;
- Node *right;
- Node *colRoot; //列首
- int row; //所在行
- int sum; //此列节点总数
- };
- #define R 729
- #define C 324
- class Dlx
- {
- public:
- Node *nodes,*row,*col,*head;//可用节点,行首,列首,总头节点
- int rowNum,colNum,nodeCount;//行数,列数,总节点数
- int *result,resultCount;//结果,结果行数
- Dlx()
- {
- nodes=new Node[R*C];//直接用数组竟然运行不起,栈溢出了,还得放在堆里
- row=new Node[R];
- col=new Node[C+1];
- result=new int[R];
- }
- ~Dlx()
- {
- delete []nodes;
- delete []row;
- delete []col;
- delete []result;
- }
- void init(int r,int c);//初始化
- void cover(Node *t);//覆盖一列
- void uncover(Node *t);//取消覆盖
- bool solove(int k=0);//搜索出结果
- void addNode(int r,int c);//添加一个节点
- };
- void Dlx::init(int r,int c)
- {
- int i;
- rowNum=r;
- colNum=c;
- //将各列连起来,col[colNum]为总头节点
- for(i=0;i<=colNum;i++)
- {
- col[i].up=col[i].down=col+i;
- col[i].left=col + (i+colNum)%(1+colNum);
- col[i].right=col + (i+1)%(1+colNum);
- col[i].sum=0;
- }
- head=col+colNum;
- //将各行节点数清零
- for(i=0;i<rowNum;i++)
- {
- row[i].up=row[i].down=row[i].left=row[i].right=row[i].colRoot=row+i;
- }
- nodeCount=0;//总节点数清零
- }
- void Dlx::addNode(int r,int c)
- {
- nodes[nodeCount].up=col[c].up;
- nodes[nodeCount].down=col+c;
- nodes[nodeCount].left=row[r].left;
- nodes[nodeCount].right=row+r;
- nodes[nodeCount].row=r;
- nodes[nodeCount].colRoot=col+c;
- col[c].up=col[c].up->down=row[r].left=row[r].left->right=nodes+nodeCount++;
- col[c].sum++;
- }
- void Dlx::cover(Node *t)
- {
- Node *p,*q;
- t->left->right=t->right;
- t->right->left=t->left;
- for(p=t->down;p!=t;p=p->down)
- {
- for(q=p->right;q!=p;q=q->right)
- {
- q->up->down=q->down;
- q->down->up=q->up;
- q->colRoot->sum--;
- }
- }
- }
- void Dlx::uncover(Node *t)
- {
- Node *p,*q;
- for(p=t->up;p!=t;p=p->up)
- {
- for(q=p->left;q!=p;q=q->left)
- {
- q->up->down=q->down->up=q;
- q->colRoot->sum++;
- }
- }
- t->left->right=t->right->left=t;
- }
- bool Dlx::solove(int k)
- {
- //是否还有未覆盖的列
- if(head->right==head)
- {
- //记录完成覆盖所用行数
- resultCount=k;
- return true;
- }
- Node *pMin,*p,*q;
- //找到节点数最少的一列,并覆盖
- for(pMin=head->right,p=pMin->right;p!=head;p=p->right)
- {
- if(pMin->sum>p->sum)
- pMin=p;
- }
- cover(pMin);
- for(p=pMin->down;p!=pMin;p=p->down)
- {
- result[k]=p->row;
- //选定此列上的一个节点,将此节点所在行上所有节点的对应列进行覆盖
- for(q=p->right;q!=p;q=q->right)
- cover(q->colRoot);
- if(solove(k+1))
- return true;
- //如果不能成功,则取消覆盖
- for(q=p->left;q!=p;q=q->left)
- uncover(q->colRoot);
- }
- uncover(pMin);
- return false;
- }
- int getRowIndex(int rowNum)
- {
- int num = rowNum%9;
- int rowIndex = rowNum / 81;
- return 81 + rowIndex*9 + num;
- }
- int getColIndex(int rowNum)
- {
- int num = rowNum%9;
- int index = rowNum/9; //位置
- int colIndex = index%9;
- return 162 + colIndex*9+num;
- }
- int getSquareIndex(int rowNum)
- {
- int num = rowNum%9;
- int index = rowNum/9; //位置
- int rowIndex = index / 9;
- int colIndex = index%9;
- int squareIndex = int(rowIndex/3)*3 + colIndex/3;
- return 243 + squareIndex*9+num;
- }
- int main3()
- {
- int i,j;
- int node4=0;
- char str[82];
- Dlx dlx;
- //cin>>n;
- dlx.init(729,324);
- //for(i=0;i<9;i++)
- //{
- // cin>> (str+i*9);
- //}
- //......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
- const char *input = ".2738..1..1...6735.......293.5692.8...........6.1745.364.......9518...7..8..6534.";
- strcpy(str,input);
- for(i=0;i<729;i++)
- {
- //cout << "row=>" << i << "\tcol=> 位置" << i/9 <<"\t行"<<81+i/9/9*9+i%9<<"\t列"<<162+i/9%9*9+i%9<<"\t块"<< 243+(i/9/9/3*3+i/9%9/3)*9+i%9;
- //cout << "row=>" << i << "\tcol=> 位置" << i/9 <<"\t行"<<getRowIndex(i)<<"\t列"<<getColIndex(i)<<"\t块"<<getSquareIndex(i);
- if(str[i/9]=='.' || str[i/9]-'1'==i%9)
- {
- node4++;
- int rowIndex = i;
- int colIndex = i/9;
- dlx.addNode(rowIndex,colIndex);//位置冲突
- dlx.addNode(rowIndex,getRowIndex(i));//行冲突
- dlx.addNode(rowIndex,getColIndex(i));//列冲突
- dlx.addNode(rowIndex,getSquareIndex(i));//块冲突
- // cout << "\t<=";
- }
- //cout << endl;
- }
- if(dlx.solove())
- {
- //结果存到字符串中
- for(i=0;i<81;i++)
- {
- j=dlx.result[i];
- str[j/9]='1'+j%9;
- }
- //输出字符串
- for(i=0;i<9;i++)
- {
- for(j=0;j<9;j++)
- cout<<str[i*9+j];
- cout<<endl;
- }
- }
- return 0;
- }
附,生成数独终盘布局的Flex代码
- <?xml version="1.0" encoding="utf-8"?>
- <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
- xmlns:s="library://ns.adobe.com/flex/spark"
- xmlns:mx="library://ns.adobe.com/flex/mx">
- <fx:Declarations>
- <!-- Place non-visual elements (e.g., services, value objects) here -->
- </fx:Declarations>
- <fx:Script>
- <![CDATA[
- import model.DancingNode;
- private var sample:String="1279,2367,1369,24789,4578,259,13458,1368,456";
- private var rowArr:Array=[];
- private var colArr:Array=[];
- private var head:DancingNode;
- private var nodes:Array=[];
- private var cnt:Array=[];
- private var answer:Array=[];
- private var answerArr:Array=[];
- private function _init(restrict:String):void
- {
- answerArr=[];
- answer=[];
- cnt=[];
- nodes=[];
- rowArr=[];
- colArr=[];
- var i:int;
- var colLen:int=18;
- var rowLen:int=restrict.split(',').join('').length;
- for(i=0;i<rowLen;i++)
- {
- var row:DancingNode = new DancingNode(i);
- rowArr.push(row);
- }
- for(i=0;i<=colLen;i++)
- {
- var col:DancingNode = new DancingNode(-1,i);
- colArr.push(col);
- cnt.push(0);
- }
- var colArrLen:int = colArr.length;
- for(i=0;i<colArr.length;i++)
- {
- var left:int = (i+colArrLen-1)%colArrLen;
- var right:int = (i+1)%colArrLen;
- DancingNode(colArr[i]).left=colArr[left];
- DancingNode(colArr[i]).right=colArr[right];
- }
- head = colArr[0];
- //create links
- var rowIndex:int=0;
- var arr1:Array = restrict.split(',');
- for(i=0;i<arr1.length;i++)
- {
- var arr2:Array = String(arr1[i]).split('');
- for(var j:int=0;j<arr2.length;j++)
- {
- var colIndex1:int=i+1;
- var colIndex2:int=9+int(arr2[j]);
- addNode(rowIndex,colIndex1,i,int(arr2[j]));
- addNode(rowIndex,colIndex2,i,int(arr2[j]));
- rowIndex++;
- }
- }
- for(i=0;i<rowLen;i++)
- {
- DancingNode(rowArr[i]).left.right=DancingNode(rowArr[i]).right;
- DancingNode(rowArr[i]).right.left=DancingNode(rowArr[i]).left;
- }
- }
- private function addNode(r:int,c:int,r1:int,c1:int):void
- {
- var node:DancingNode = new DancingNode(r,c);
- node.rowValue=r1;
- node.colValue=c1;
- node.up = colArr[c].up;
- node.down = colArr[c];
- node.left = rowArr[r].left;
- node.right = rowArr[r];
- cnt[c]++;
- colArr[c].up=colArr[c].up.down=rowArr[r].left=rowArr[r].left.right=node;
- nodes.push(node);
- }
- private function remove(node:DancingNode):void
- {
- //trace("remove=>",node.col);
- node.left.right = node.right;
- node.right.left = node.left;
- for(var p:DancingNode=node.down;p!=node;p=p.down)
- {
- for(var q:DancingNode=p.right;q!=p;q=q.right)
- {
- q.up.down=q.down;
- q.down.up=q.up;
- cnt[q.col]--;
- }
- }
- }
- private function resume(node:DancingNode):void
- {
- //trace("resume=>",node.col);
- for(var p:DancingNode=node.down;p!=node;p=p.down)
- {
- for(var q:DancingNode=p.right;q!=p;q=q.right)
- {
- q.up.down=q;
- q.down.up=q;
- cnt[q.col]++;
- }
- }
- node.left.right = node;
- node.right.left = node;
- }
- private function dancing(depth:int):Boolean
- {
- //是否还有未覆盖的列
- if(head.right==head)
- {
- var arr:Array=[];
- for(var i:int=0;i<answer.length;i++)
- {
- var node:DancingNode=answer[i];
- arr[node.rowValue]=node.colValue;
- }
- answerArr.push(arr);
- return true;
- }
- var pMin:DancingNode;
- var p:DancingNode;
- //找到节点数最少的一列,并覆盖
- for(pMin=head.right,p=pMin.right;p!=head;p=p.right)
- {
- if(cnt[pMin.col] > cnt[p.col])
- {
- pMin = p;
- }
- }
- remove(pMin);
- var q:DancingNode;
- for(p=pMin.down;p!=pMin;p=p.down)
- {
- //选定此列上的一个节点,将此节点所在行上所有节点的对应列进行覆盖
- answer[depth]=p;
- for(q=p.right;q!=p;q=q.right)
- {
- remove(colArr[q.col]);
- }
- if(dancing(depth+1))
- {
- if(answerArr.length > 10)
- {
- return true;
- }
- }
- for(q=p.left;q!=p;q=q.left)
- {
- resume(colArr[q.col]);
- }
- }
- resume(pMin);
- if(answerArr.length > 0)
- {
- return true;
- }
- return false;
- }
- private function getSudokuLine(restricts:Array):Array
- {
- var arr:Array=[];
- for(var i:int=0;i<restricts.length;i++)
- {
- arr.push((restricts[i] as Array).join(''));
- }
- _init(arr.join(','));
- if(dancing(0))
- {
- var line:Array = answerArr[int(answerArr.length*Math.random())];
- trace('getSudokuLine,answer length=>',answerArr.length);
- return line;
- }
- return [];
- }
- //得到随机的1到9的排列
- private function getRandomArr(value:int):Array
- {
- var bak:Array = [];
- for(var i:int=1;i<=value;i++)
- {
- bak.push(i);
- }
- var randLine:Array=[];
- while(bak.length>0)
- {
- var index:int = bak.length*Math.random();
- randLine.push(bak[index]);
- bak.splice(index,1);
- }
- return randLine;
- }
- public function createFullSudoku():Array
- {
- var sudokuArr:Array=[];
- while(sudokuArr.length < 9)
- {
- sudokuArr=[];
- sudokuArr.push(getRandomArr(9));
- for(var i:int=0;i<8;i++)
- {
- var restricts:Array = getRestricts(sudokuArr);
- if(restricts.length==0)
- {
- break;
- }
- var line:Array = getSudokuLine(restricts);
- if(line.length==0)
- {
- break;
- }
- sudokuArr.push(line);
- }
- }
- return sudokuArr;
- }
- private function getRestricts(curLayout:Array):Array
- {
- var i:int;
- var ret:Array=[];
- for(i=0;i<9;i++)
- {
- var arr:Array=getCandidateNums(curLayout,i);
- if(arr.length==0)
- {
- return [];
- }
- ret.push(arr);
- }
- return ret;
- }
- //根据当前布局curLayout,得到index列的候选数集合
- private function getCandidateNums(curLayout:Array,index:int):Array
- {
- var i:int;
- var line:Array = [0,1,2,3,4,5,6,7,8,9];
- if(curLayout.length==0)
- {
- return [1,2,3,4,5,6,7,8,9];
- }
- //列排除
- for(i=0;i<curLayout.length;i++)
- {
- line[curLayout[i][index]]=0;
- }
- //九宫格排除
- var col3_3:int = index/3;
- var row3_3:int = curLayout.length/3;
- var inRow3_3:int = curLayout.length%3;
- for(i=row3_3*3;i<row3_3*3+inRow3_3;i++)
- {
- line[curLayout[i][col3_3*3] ]=0;
- line[curLayout[i][col3_3*3+1] ]=0;
- line[curLayout[i][col3_3*3+2] ]=0;
- }
- var ret:Array=[];
- for(i=0;i<line.length;i++)
- {
- if(line[i]!=0)
- {
- ret.push(i);
- }
- }
- return ret;
- }
- private function createSudoku():void
- {
- var arr:Array=createFullSudoku();
- var arr2:Array=[];
- for(var i:int=0;i<arr.length;i++)
- {
- arr2.push((arr[i] as Array).join(','));
- }
- area.text = arr2.join('\n');
- }
- ]]>
- </fx:Script>
- <s:VGroup horizontalCenter="0" verticalCenter="0">
- <s:TextArea width="300" height="300" id="area"/>
- <s:Button label="get full sudoku" click="createSudoku()"/>
- </s:VGroup>
- </s:WindowedApplication>
- package model
- {
- public class DancingNode
- {
- public var row:int;
- public var col:int;
- public var rowValue:int;
- public var colValue:int;
- public var up:DancingNode;
- public var down:DancingNode;
- public var left:DancingNode;
- public var right:DancingNode;
- public function DancingNode(r:int=-1,c:int=-1)
- {
- row = r;
- col = c;
- up = this;
- down = this;
- left = this;
- right = this;
- }
- }
- }
- dancing link 资料
- dancing link
- dancing link(poj3076)
- poj 3076 Dancing Link
- 【算法杂谈】Dancing Link
- Dancing Link讲解
- Dancing Link 详解(转载)
- Dancing link ZOJ3209
- Dancing Link 精确覆盖模版
- HDU+2295+Dancing+link++++二分
- dancing link 解决八皇后
- Dancing Link基础题2
- (转)数独之Dancing Link解法
- HDU 3529 Dancing link 重复覆盖
- hdu 3498 Dancing link 重复覆盖
- Dancing link 与精确匹配问题
- POJ-3076 Sudoku(Dancing Link 算法)
- zoj-3209 Treasure Map【Dancing link x】
- POJ 1742 Coins(多重背包)
- java 1加到100
- XDOJ1170 - 蛋疼的游戏
- hdu 3682
- 在线分享Oracle尖峰时刻--2014年中秋节尖峰在线福利!
- dancing link 资料
- POJ 2063Investment(完全背包)
- Java开发中的23种设计模式详解
- 【BZOJ】【P2463】【中山市选2009】【题解】【谁能赢呢?】
- 使用crontab定时执行php
- HDU-3085-Nightmare Ⅱ(双向BFS)
- 杭电1279 Stone Game(经典博弈)
- Codeforces Round #253 (Div. 2) C Borya and Hanabi (思维+bitmask)
- 懒惰的风纪委Elaine(宁波工程学院)