两个吃奶一个添下面视频_人妻第一页香蕉网_欧美xxxx少妇_妺妺窝人体色www婷婷

前面的文字介紹了舞蹈鏈,這里就不詳細(xì)描述什么是舞蹈鏈了,舞蹈鏈(Dancing links)是一種數(shù)據(jù)結(jié)構(gòu),可以用來(lái)實(shí)現(xiàn)X算法,以解決精確覆蓋問(wèn)題。本篇的內(nèi)容主要把舞蹈鏈Dancing links應(yīng)用于實(shí)際問(wèn)題,通過(guò)實(shí)踐才能幫助大家更好的理解,片面的了解理論知識(shí),最終不能落到實(shí)處,也就沒(méi)有意義了。

當(dāng)然,把Dancing links應(yīng)用于實(shí)際問(wèn)題時(shí),只有一個(gè)難點(diǎn),就是如何把具體的問(wèn)題轉(zhuǎn)換為可以精確覆蓋的01矩陣模型,一旦完成了這個(gè)步后,直接套用模板就可以解決問(wèn)題了。


應(yīng)用之一:傷腦筋十二塊               

傷腦筋十二塊是Dancing links精確覆蓋的典型應(yīng)用,理解起來(lái)最容易      

傷腦筋十二塊

圖1:12片5格骨牌的拼圖

題目描述:         

給你12個(gè)如上圖的5格骨牌,如何讓程序拼出如上的“口”字圖形。          

上圖是題目的一個(gè)答案,你知道程序如何得到這個(gè)答案的嗎?沒(méi)錯(cuò),就是用Dancing links的精確覆蓋。        

我們想象一個(gè)有72列的矩陣,其中12列是12個(gè)骨牌,剩下60列是60個(gè)非中心部分的格子。         

所有可能的行來(lái)代表在一塊骨牌在棋盤(pán)上得放置方案;每行有一些“1”,用來(lái)標(biāo)識(shí)被覆蓋的格子,5個(gè)1標(biāo)識(shí)一個(gè)骨牌放置的位置(恰有1568個(gè)這樣的行)             

我們將最前面的12列命名為F,I,L,P,N,T,U,V,W,X,Y,Z,并且我們可以用兩個(gè)數(shù)字i,j給矩陣中對(duì)應(yīng)棋盤(pán)上的第i行第j列格子的那一列命名。通過(guò)給出那些出現(xiàn)了“1”的列的名字,可以很方便地表示每一行。        

例如,圖2就是與下面12行的對(duì)應(yīng)的精確覆蓋。            

精確覆蓋

1568個(gè)行指的是12個(gè)骨牌可放置方案的總和,比如長(zhǎng)條骨牌I共有64種放置方案,1568中就包含了這64種                              這1568行中,每行都有6個(gè)1,分布在72個(gè)列中                                 

這個(gè)矩陣的構(gòu)造思路是:                                  

首先找約束關(guān)系,這里只有兩個(gè)約束關(guān)系,                              

(1)12個(gè)骨牌,每種只有1個(gè);                             

(2)60個(gè)空格中,一個(gè)位置只能放一種骨牌(否則就要重疊著放了);                            

因?yàn)榈谝粋€(gè)約束關(guān)系,有了12個(gè)列來(lái)區(qū)分骨牌種類(lèi),因?yàn)榈诙€(gè)約束關(guān)系,有了60個(gè)選5個(gè)來(lái)表示骨牌放置 。


應(yīng)用之二:數(shù)獨(dú)問(wèn)題 sudoku           

解數(shù)獨(dú),生成數(shù)獨(dú),都可以使用精確覆蓋,要把數(shù)獨(dú)問(wèn)題構(gòu)造成01矩陣還是有一定的難度。

首先找約束關(guān)系,這里只有四個(gè)約束關(guān)系,

(1)81個(gè)格子中每個(gè)格子只能放一個(gè)數(shù)字  

(2)每一行的數(shù)字不能重復(fù)      

(3)每一列的數(shù)字不能重復(fù)      

(4)每一九宮內(nèi)的數(shù)字不能重復(fù)

數(shù)獨(dú)問(wèn)題 sudoku

四個(gè)約束關(guān)系中,每個(gè)約束對(duì)應(yīng)一個(gè)列域,對(duì)于第二個(gè)約束關(guān)系,數(shù)獨(dú)中共有9行,每行可以填9個(gè)不同的數(shù)字;

因此第二個(gè)列域,共有9* 9,81個(gè)列,依此類(lèi)推,數(shù)獨(dú)問(wèn)題共有列324個(gè);

由于81個(gè)格子,每個(gè)格子都最多有9種選擇,所以行最多有81*9=729行;

這樣01矩陣的每行都有4個(gè)1,第一個(gè)1分布在1到81列,第二個(gè)1分布在82到162列,第三個(gè)1分布在163到243列;

最后一個(gè)1分布在其余列區(qū)域。 

思考:為什么不能這樣構(gòu)造01矩陣,用5個(gè)1,第一個(gè)1表示格子序號(hào),有81個(gè)列,第二個(gè)1表示數(shù)字,從1到9有9個(gè)列,第三個(gè)1表示行號(hào),有9行,第四個(gè)1表示列號(hào)也有9個(gè),第五個(gè)1表示九宮格序號(hào),也有9個(gè),這樣共有117列。            

為了便于理解,舉個(gè)例子             

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         

如上數(shù)獨(dú)有空格40個(gè),已知格子41個(gè),把這個(gè)數(shù)獨(dú)構(gòu)造成01矩陣,矩陣的行有40*9+41共401行。

對(duì)于第一個(gè)數(shù)字9,在1到81列的第一列,在82到162列的第9個(gè),即90列,在163列到243列的第9個(gè),在244到324列的第9個(gè)各占一個(gè)1 ;

對(duì)于第三個(gè)數(shù)字0,由于有9個(gè)選擇,所以在構(gòu)造01矩陣時(shí),要向矩陣插入9個(gè)行,來(lái)表示各種可能;

對(duì)于第四個(gè)數(shù)字8,它在二行四列,把這個(gè)數(shù)字寫(xiě)入dancing link的網(wǎng)狀數(shù)據(jù)結(jié)構(gòu)時(shí),需要新增四個(gè)節(jié)點(diǎn),這四個(gè)節(jié)點(diǎn)都在同一行,它們的列序號(hào)分別為13, 81 + 9 + 8 - 1,81 + 81 + 3 * 9 + 8 - 1,81 + 81 + 81 + 9  + 8 - 1,  序號(hào)是從0開(kāi)始的,所有要減去一。

現(xiàn)在假設(shè),2行6列的空格是數(shù)字8,那么這個(gè)數(shù)字也會(huì)對(duì)應(yīng)四個(gè)節(jié)點(diǎn),列序號(hào)分別為15,81 + 9 + 8 - 1, 81 + 81 + 5 * 9 + 8 - 1, 81 + 81 + 81 + 9 + 8 - 1,

可以看到, 這兩個(gè)8的行域,九宮格域都是相同的,表示這兩個(gè)數(shù)字的行和九宮格都相沖了,四個(gè)列域,只要有一個(gè)相沖,兩條記錄就不能共存。這兩個(gè)8顯然不能共存。

數(shù)獨(dú)還有一個(gè)變種,對(duì)角線數(shù)獨(dú),兩條對(duì)角線的數(shù)字也不能重復(fù),這時(shí)構(gòu)造01矩陣模型時(shí),就需要額外增加兩個(gè)列域,左對(duì)角線域,右對(duì)角線域。增加的兩個(gè)列域都只有9列,

對(duì)于1行1列的數(shù)字,會(huì)在01矩陣模型中對(duì)應(yīng)5個(gè)節(jié)點(diǎn),

對(duì)于2行3列的數(shù)字,由于不位于兩條對(duì)角線上,會(huì)在01矩陣模型中只對(duì)應(yīng)4個(gè)節(jié)點(diǎn), 

對(duì)于5行5列的數(shù)字,恰好在兩條對(duì)角線的交匯處,會(huì)在01矩陣模型中對(duì)應(yīng)6個(gè)節(jié)點(diǎn)

對(duì)于數(shù)獨(dú)的生成             

總體思路是一行一行的生成,第一行可以用一個(gè)隨機(jī)的1到9的排列,接下來(lái)的8行,每行都要用dancinglink求解可行的排序             

(1)先對(duì)1到9這9個(gè)數(shù)進(jìn)行隨機(jī)排列,把這個(gè)排列作為數(shù)獨(dú)終盤(pán)布局的第一行             

(2)自己寫(xiě)函數(shù)篩選出下一行,每個(gè)格子可以填寫(xiě)的數(shù)字集合,篩選時(shí)不用考慮行沖突        

比如對(duì)于排列5,9,7,4,2,6,8,3,1             

篩選結(jié)果如下:123468,123468,123468,135789,135789,135789,245679,245679,245679  

表示對(duì)于下一行的1,2,3列,可以選擇的數(shù)字集合有1,2,3,4,6,8.           

下一行的4,5,6列,可以選擇的數(shù)字集合有1,3,5,7,8,9       

下一行的7,8,9列,可以選擇的數(shù)字集合有2,4,5,6,7,9       

這時(shí),構(gòu)造01矩陣,就只有2個(gè)約束關(guān)系           

(1)對(duì)于下一行的9個(gè)格子,每個(gè)格子只能放一個(gè)數(shù)字    

(2)對(duì)于下一行的9個(gè)格子中的數(shù)字,每個(gè)數(shù)字都不能重復(fù) 

因?yàn)榈?個(gè)和4個(gè)約束,已經(jīng)在篩選時(shí)考慮進(jìn)去,這里不需再多此一舉          

這時(shí)的01矩陣,列有9+ 9=18個(gè),行有6* 9 = 54行(6+6+6+6+6+6+6+6+6)。


應(yīng)用之三:N皇后         

N皇后問(wèn)題也可以轉(zhuǎn)換為Dancing links的精確覆蓋問(wèn)題,這里只講如何把n皇后問(wèn)題轉(zhuǎn)換為01矩陣,首先有四個(gè)約束關(guān)系

(1)所有皇后不能在同一行       

(2)所有皇后不能在同一列       

(3)所有皇后不能在同一左斜線             

(4)所有皇后不能在同一右斜線             

為了便于理解,舉個(gè)例子:n=8時(shí),有8行,8列,15個(gè)左斜線,15個(gè)右斜線(2*n-1),這樣構(gòu)造的矩陣有46個(gè)列,8*8=64個(gè)行,矩陣的每行都有4個(gè)1,分別分布在行域,列域,左斜線域,右斜線域,在編程求解這個(gè)問(wèn)題時(shí),需要做一點(diǎn)變通,因?yàn)樽笮本€域,右斜線域的列不可能被全部覆蓋,因此只需行域和列域被完全覆蓋就算找到問(wèn)題的一個(gè)解了。

附:

dancing LInks 求解數(shù)獨(dú)的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; //此列節(jié)點(diǎn)總數(shù)
};
 
#define R 729
#define C 324
 
 
class Dlx
{
public:
	Node *nodes,*row,*col,*head;//可用節(jié)點(diǎn),行首,列首,總頭節(jié)點(diǎn)
	int rowNum,colNum,nodeCount;//行數(shù),列數(shù),總節(jié)點(diǎn)數(shù)
	int *result,resultCount;//結(jié)果,結(jié)果行數(shù)
 
	Dlx()
	{
		nodes=new Node[R*C];//直接用數(shù)組竟然運(yùn)行不起,棧溢出了,還得放在堆里
		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);//搜索出結(jié)果
	void addNode(int r,int c);//添加一個(gè)節(jié)點(diǎn)
};
 
 
void Dlx::init(int r,int c)
{
	int i;
	
	rowNum=r;
	colNum=c;
	//將各列連起來(lái),col[colNum]為總頭節(jié)點(diǎn)
	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;
 
	//將各行節(jié)點(diǎn)數(shù)清零
	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;//總節(jié)點(diǎn)數(shù)清零
}
 
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)
	{
		//記錄完成覆蓋所用行數(shù)
		resultCount=k;
		return true;
	}
	
	Node *pMin,*p,*q;
	//找到節(jié)點(diǎn)數(shù)最少的一列,并覆蓋
	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;
		//選定此列上的一個(gè)節(jié)點(diǎn),將此節(jié)點(diǎn)所在行上所有節(jié)點(diǎn)的對(duì)應(yīng)列進(jìn)行覆蓋
		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())
	{
		//結(jié)果存到字符串中
		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;
}
點(diǎn)贊(0)

C語(yǔ)言網(wǎng)提供由在職研發(fā)工程師或ACM藍(lán)橋杯競(jìng)賽優(yōu)秀選手錄制的視頻教程,并配有習(xí)題和答疑,點(diǎn)擊了解:

一點(diǎn)編程也不會(huì)寫(xiě)的:零基礎(chǔ)C語(yǔ)言學(xué)練課程

解決困擾你多年的C語(yǔ)言疑難雜癥特性的C語(yǔ)言進(jìn)階課程

從零到寫(xiě)出一個(gè)爬蟲(chóng)的Python編程課程

只會(huì)語(yǔ)法寫(xiě)不出代碼?手把手帶你寫(xiě)100個(gè)編程真題的編程百練課程

信息學(xué)奧賽或C++選手的 必學(xué)C++課程

藍(lán)橋杯ACM、信息學(xué)奧賽的必學(xué)課程:算法競(jìng)賽課入門(mén)課程

手把手講解近五年真題的藍(lán)橋杯輔導(dǎo)課程

Dotcpp在線編譯      (登錄可減少運(yùn)行等待時(shí)間)