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;
}

猜你喜欢

转载自blog.csdn.net/qq_41090676/article/details/84633790