1. 程式人生 > >POJ-1724 深搜剪枝

POJ-1724 深搜剪枝

這道題目如果資料很小的話。我們通過這個dfs就可以完成深搜:


void dfs(int s)
{
	if (s==N)
	{
		minLen=min(minLen,totalLen);
		return ;
	}
	for (int i=0;i<G[s].size();i++)
	{
		Road r=G[s][i];
		if (r.t+totalCost>K)
			continue;
		if (!visited[r.d])
		{
			visited[r.d]=1;
			totalLen+=r.L;
			totalCost+=r.t;
			dfs(r.d);
			totalCost-=r.t;
			totalLen-=r.L;
			visited[r.d]=0;
		}
		
	}
}

我們可以看一下這個程式碼,意思就是說,如果找邊的時候,我們已經搜尋到了終點,也就是s==N的時候,我們就直接改寫minLen,然後返回到上一層,進行totalCost,totalLen和visited陣列的返回工作,因為我們這次走的是這一條路,當我們返回的時候,就將這條路的終點標記全部還原,因為從這條路的起始點還可能會有其它的路,如果不把它還原的話,其它的路就被封死了,深搜就無法進行的很完全了,不能說是遍歷了。

我們對於以s為起點的邊進行遍歷,發現邊r的花費加上之前的總花銷已經超過K,總錢數了,我們就跳過這一條邊,這是第一次剪枝。

如果我們沒有訪問過邊r的終點d時,我們就把它訪問位設定為1,總路程加上r邊長,總花費加上r邊的過路費,然後深搜d。

這是可以通過一些較小的資料的,但是這道題中的資料很大,而dfs中又做了很多的無用功,所以我們進行以下的剪枝:

如果d點沒有被訪問過,我們就判斷如果這次走到點d的時候,總路程已經超過minLen了,也就是之前找到的最短路,我們就跳過這個終點的深搜,我們直接不走這條路了。

這個剪枝還是不夠,所以我們拿空間換取時間,我們設定一個minL[110][10010]陣列,minL[k][m]表示之前走到點k並且花費為m的最短長度。

如果我們這次走到點k,並且花銷為m,但是我們的路程已經大於這個最短長度了,我們就跳出這重迴圈,執行迴圈的下一次。

因為它的意思,也就是說,我們每走過一個點,我們就進行一次比較,確保我們不花相同的錢,走更遠的路,這個剪枝極為有效,直接可以過。

程式碼如下:

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int N,K,R;
struct Road {
	int d,L,t;
};
vector < vector<Road> > G(110);

int minLen;
int minL[110][10010];
int totalLen;	
int totalCost;
int visited[110];


void dfs(int s)
{
	if (s==N)
	{
		minLen=min(minLen,totalLen);
		return ;
	}
	for (int i=0;i<G[s].size();i++)
	{
		Road r=G[s][i];
		if (r.t+totalCost>K)
			continue;
		if (!visited[r.d])
		{
			if (totalLen+r.L>=minLen)
				continue;
			if (totalLen+r.L>=minL[r.d][r.t+totalCost])
				continue;
			visited[r.d]=1;
			totalLen+=r.L;
			minL[r.d][r.t+totalCost]=totalLen;
			totalCost+=r.t;
			dfs(r.d);
			totalCost-=r.t;
			totalLen-=r.L;
			visited[r.d]=0;
		}
		
	}
}

int main()
{
	cin>>K>>N>>R;
	for (int i=0;i<R;i++) {
		int s;
		Road r;
		cin>>s>>r.d>>r.L>>r.t;
		if (s!=r.d) {
			G[s].push_back(r);
		}
	}
	memset(visited,0,sizeof(visited));
	for (int i=0;i<110;i++) {
		for (int j=0;j<10010;j++) {
			minL[i][j]=1<<30;
		}
	}
	totalLen=0;
	totalCost=0;
	minLen=1<<30;
	visited[1]=1;
	dfs(1);
	if (minLen<(1<<30))
		cout<<minLen<<endl;
	else cout<<-1<<endl;
	return 0;
}