dfs的剪枝思维和例题分析
ROADS(POJ 1724)
解题思路:
从城市1开始深度优先遍历整个图,找到所有能过到N的走法,选一个最优的
代码(1):
#include#include #include using namaspace std; int K, N, R; struct Road { int d, l, t; }; vector< vector < Road > > G(110); //这个其实就是一个二维数组,但是不会一开始就把数组的大小限制掉 //这样每次输入一个道路信息,就直接可以把道路信息压入容器就好了 int minLen; int totalLen; int totalCost; int visited[110]; //记录是不是每一条路都走过 void dfs(int s){ if(s == N){ //可以用max和min来更新minLen这个也不是第一次知道了 minLen = min(totalLen,minLen); return; } for(int i = 0;i ){ //这个G[s]中储存的全部都是与s也就是起点有相连的结点 Road r = G[s][i]; //用一个路来储存边信息 //首先判断价格会不会超过 if(totalCost+r.t>K){ continue; } if(!visited[r.d]){ //如果没有走过的话 totalLen+=r.L; totalCost+=r.t; visited[r.d] = 1; dfs(r.d); //现在这条路走完了,在返回的时候要取消之前的标记 //否则上面会有路变得不通 visited[r.d] = 0; totalLen-=r.L; totalCost+=r.t; } } } int main(){ cin>> K >> N >>R; for(int i = 0;i ){ //后面是输入过程 int s; //s是起点 Road r; cin>>s >>r.d>>r.l>>r.t; //接下来要把边放到容器里 if(s != r.d){ //这个r不是起点 G[s].push_back(r);容器里面 //把这个道路的信息放到 } } memset(visited,0,sizeof(visited)); totalLen = 0; minLen = 1<<30; //这个就是把1右移了30位,这样就是一个很大的数 totalCost = 0; visited[1] = 1; //如果是1的话 dfs(1); //可能找不到最小值,如果找到了最小值 if(minLen < ( 1 << 30 )) { cout< endl; }else{ cout<<"-1"<<endl; } return 0; }
上面这段代码是没有用到剪枝的(其实这个是有剪枝的,比如因为价钱的超出而continue掉来节省时间),所以如果数值大的话就会超时。那么我可以这样优化:当在深度优先搜索某一条路的时候,只要这条路的totalLen的值大于minLen,这条路没有必要走下去了,直接continue跳过就好了,这样可以大大的节省时间。
代码2:
#include#include #include using namaspace std; int K, N, R; struct Road { int d, l, t; }; vector< vector < Road > > G(110); //这个其实就是一个二维数组,但是不会一开始就把数组的大小限制掉 //这样每次输入一个道路信息,就直接可以把道路信息压入容器就好了 int minLen; int totalLen; int totalCost; int visited[110]; //记录是不是每一条路都走过 void dfs(int s){ if(s == N){ //可以用max和min来更新minLen这个也不是第一次知道了 minLen = min(totalLen,minLen); return; } for(int i = 0;i ){ //这个G[s]中储存的全部都是与s也就是起点有相连的结点 Road r = G[s][i]; //用一个路来储存边信息 //首先判断价格会不会超过 if(totalCost+r.t>K){ continue; } if(!visited[r.d]){ if(totalCost+r.L >= minLen){ continue; } //如果没有走过的话 totalLen+=r.L; totalCost+=r.t; visited[r.d] = 1; dfs(r.d); //现在这条路走完了,在返回的时候要取消之前的标记 //否则上面会有路变得不通 visited[r.d] = 0; totalLen-=r.L; totalCost+=r.t; } } } int main(){ cin>> K >> N >>R; for(int i = 0;i ){ //后面是输入过程 int s; //s是起点 Road r; cin>>s >>r.d>>r.l>>r.t; //接下来要把边放到容器里 if(s != r.d){ //这个r不是起点 G[s].push_back(r);容器里面 //把这个道路的信息放到 } } memset(visited,0,sizeof(visited)); totalLen = 0; minLen = 1<<30; //这个就是把1右移了30位,这样就是一个很大的数 totalCost = 0; visited[1] = 1; //如果是1的话 dfs(1); //可能找不到最小值,如果找到了最小值 if(minLen < ( 1 << 30 )) { cout< endl; }else{ cout<<"-1"<<endl; } return 0; }
和上面的比就优化了一个小小的地方,只要totalLen加上这条路的长度超过minLen就剪枝。但是这样发现如果数据再大一点的话还是过不了,还是超时,说明这个剪枝不够强,还要更多的剪枝。
代码3(保存中间计算结果):
#include#include #include using namaspace std; int K, N, R; struct Road { int d, l, t; }; vector< vector < Road > > G(110); //这个其实就是一个二维数组,但是不会一开始就把数组的大小限制掉 //这样每次输入一个道路信息,就直接可以把道路信息压入容器就好了 int minLen; int midL[110][10010] ; int totalLen; int totalCost; int visited[110]; //记录是不是每一条路都走过 void dfs(int s){ if(s == N){ //可以用max和min来更新minLen这个也不是第一次知道了 minLen = min(totalLen,minLen); return; } for(int i = 0;i ){ //这个G[s]中储存的全部都是与s也就是起点有相连的结点 Road r = G[s][i]; //用一个路来储存边信息 //首先判断价格会不会超过 if(totalCost+r.t>K){ continue; } if(!visited[r.d]){ if(totalCost+r.L >= minLen){ continue; } if(totalLen + r.L >= midL[r.d][totalCost+r.t]){ continue; } minL[r.d][totalCost+r.t] = totalLen+r.L; //如果没有走过的话 totalLen+=r.L; totalCost+=r.t; visited[r.d] = 1; dfs(r.d); //现在这条路走完了,在返回的时候要取消之前的标记 //否则上面会有路变得不通 visited[r.d] = 0; totalLen-=r.L; totalCost+=r.t; } } } int main(){ cin>> K >> N >>R; for(int i = 0;i ){ //后面是输入过程 int s; //s是起点 Road r; cin>>s >>r.d>>r.l>>r.t; //接下来要把边放到容器里 if(s != r.d){ //这个r不是起点 G[s].push_back(r);容器里面 //把这个道路的信息放到 } } memset(visited,0,sizeof(visited)); totalLen = 0; minLen = 1<<30; //这个就是把1右移了30位,这样就是一个很大的数 totalCost = 0; visited[1] = 1; //如果是1的话 for(int i = 0;i<110;i++){ for(int j = 0;j<10010;j++){ min[i][j] = 1<<30; } } dfs(1); //可能找不到最小值,如果找到了最小值 if(minLen < ( 1 << 30 )) { cout< endl; }else{ cout<<"-1"<<endl; } return 0; }