1003. Emergency (25)

http://www.patest.cn/contests/pat-a-practise/1003

深入研究

本题适用dij+dfs,其中dij用来找出所有的最佳路径,而dfs根据第2,3,4...标出算出答案

首先用dij算法,根据各边权值算出最短路径,并用vector <int>pre [max]记住每条最短路径的前驱结点

注意点

1.pre初始化时,每个pre[i]存放i本身,这是为了后面dfs找到递归出口

for(int i=0;i<n;i++){
		pre[i].push_back(i);
}
2.有优于权值的边出现时,要清空pre,因为原来记录的那些已经不是最佳了,而对于权值相等,则要记录在pre中

if(cost[u]>cost[newp]+c || cost[u]==-1){
	pre[u].clear();
	cost[u]=cost[newp]+c;
	pre[u].push_back(newp);
}else if(cost[u]==cost[newp]+c){
	pre[u].push_back(newp);
}

完整dij

void dij(int x){
    //方便后面dfs
    for(int i=0;i<n;i++){
        pre[i].push_back(i);
    }
    int newp=x;
    vis[newp]=1;
    cost[newp]=0;
    for(int i=0;i<n-1;i++){
        for(int j=0;j<adj[newp].size();j++){
            int u=adj[newp][j].next;
            int c=adj[newp][j].wei;
            if(vis[u]==0){
                if(cost[u]>cost[newp]+c || cost[u]==-1){
                    pre[u].clear();
                    cost[u]=cost[newp]+c;
                    pre[u].push_back(newp);
                }else if(cost[u]==cost[newp]+c){
                    pre[u].push_back(newp);
                }
            }
        }
        int min=123123123;
        for(int j=0;j<n;j++){
            if(cost[j]!=-1 && vis[j]==0){
                if(cost[j]<min){
                    min=cost[j];
                    newp=j;
                }
            }
        }
        vis[newp]=1;
    }
}

dfs中有path和tmppath两种vector

path用来记录最终的path,即答案

tmppath用来记录本次的dfs得到的路径,其本质就是一个栈,相当于把dfs节点入栈,遍历结束后,出栈

但注意的是最后的出口,叶子节点无法出入栈,则对叶子节点操作时应加入出入栈过程

此过程的另一个意思就是无向图的dfs遍历可以得到拓扑排序或逆拓扑排序

dfs的出口就是访问的元素为起点,在出口时判断是否为最佳路径,计算此时tmppath的value,若更优,更新maxx和path

if(v==c1){
	ans++;
        //入栈
        tmppath.push_back(v);
        int tmp=0;//当前路径的点权
	for(int i=tmppath.size()-1;i>=0;i--){
		int a=tmppath[i];
		tmp+=val[a];
	}
	if(tmp>maxx){
		maxx=tmp;
		//vector直接赋值
		path=tmppath;
	}
	//刚刚加入节点要删除,出栈
	tmppath.pop_back();
	return;
}
dfs的递归式将访问节点入栈,dfs该节点,访问完成后弹出

//入栈
tmppath.push_back(v);
for(int i=0;i<pre[v].size();i++){
    dfs(pre[v][i],ans,maxx);
}
//出栈
tmppath.pop_back();


vector <int> path;
vector <int> tmppath;
void dfs(int v,int &ans,int &maxx){
	if(v==c1){
		ans++;
		//也自己节点单独入栈
		tmppath.push_back(v);
		//当前路径的点权
		int tmp=0;
		for(int i=tmppath.size()-1;i>=0;i--){
			int a=tmppath[i];
			tmp+=val[a];
		}
		//判断是否最优
		if(tmp>maxx){
			maxx=tmp;
			//vector直接赋值
			path=tmppath;
		}
		//叶子节点单独出栈
		tmppath.pop_back();
		return;
	}
	//入栈
	tmppath.push_back(v);
	for(int i=0;i<pre[v].size();i++){
		dfs(pre[v][i],ans,maxx);
	}
	//出栈
	tmppath.pop_back();
}

综上,此求优dfs与普通dfs完全不同

求优dfs的目的是将所有可能的起点到终点的路径全部访问,这样才能求优,依据是pre中的元素是否全部访问过,体现在for循环中

普通dfs只是求一条路径,即找到一条起点到终点的路径即可,不需要知道全部路径,无重复访问的过程,故用vis来判断是否完成dfs

因此普通dfs是无法完成求优过程的

//传入终点
int ans[MAX];
int flag[MAX];
int maxx=0;
这种dfs用vis来标记是否访问过,不记录访问路径,会出错,
因为多条路径时,存在边重合,而vis已经为1,无法再次访问
int dfs(int x,int &num,int value){
	flag[x]=1;
	value+=val[x];
	for(int i=0;i<pre[x].size();i++){
		int u=pre[x][i];
		if(u==x){
			num++;
			if(maxx<value){
				maxx=value;
			}
		}else if(flag[u]==0){
			dfs(u,num,value);
		}
	}
	return maxx;
}


完整代码

#include <cstdio>
#include <vector>
using namespace std;
#define  MAX 500
vector <int> pre[MAX];
int val[MAX];
struct node{
	int next;
	int wei;
};
vector <node> adj[MAX];
int n,m,c1,c2;
int vis[MAX];
int cost[MAX];
void init(){
	for(int i=0;i<n;i++){
		adj[i].clear();
		pre[i].clear();
		val[i]=0;
		vis[i]=0;
		cost[i]=-1;
	}
	path.clear();
}
void dij(int x){
	//方便后面dfs
	for(int i=0;i<n;i++){
		pre[i].push_back(i);
	}
	int newp=x;
	vis[newp]=1;
	cost[newp]=0;
	for(int i=0;i<n-1;i++){
		for(int j=0;j<adj[newp].size();j++){
			int u=adj[newp][j].next;
			int c=adj[newp][j].wei;
			if(vis[u]==0){
				if(cost[u]>cost[newp]+c || cost[u]==-1){
					pre[u].clear();
					cost[u]=cost[newp]+c;
					pre[u].push_back(newp);
				}else if(cost[u]==cost[newp]+c){
					pre[u].push_back(newp);
				}
			}
		}
		int min=123123123;
		for(int j=0;j<n;j++){
			if(cost[j]!=-1 && vis[j]==0){
				if(cost[j]<min){
					min=cost[j];
					newp=j;
				}
			}
		}
		vis[newp]=1;
	}
}
vector <int> path;
vector <int> tmppath;
void dfs(int v,int &ans,int &maxx){
	if(v==c1){
		ans++;
		//也自己节点单独入栈
		tmppath.push_back(v);
		//当前路径的点权
		int tmp=0;
		for(int i=tmppath.size()-1;i>=0;i--){
			int a=tmppath[i];
			tmp+=val[a];
		}
		//判断是否最优
		if(tmp>maxx){
			maxx=tmp;
			//vector直接赋值
			path=tmppath;
		}
		//叶子节点单独出栈
		tmppath.pop_back();
		return;
	}
	//入栈
	tmppath.push_back(v);
	for(int i=0;i<pre[v].size();i++){
		dfs(pre[v][i],ans,maxx);
	}
	//出栈
	tmppath.pop_back();
}
传入终点
//int ans[MAX];
//int flag[MAX];
//int maxx=0;
//这种dfs用vis来标记是否访问过,不记录访问路径,会出错,
//因为多条路径时,存在边重合,而vis已经为1,无法再次访问
//int dfs(int x,int &num,int value){
//	flag[x]=1;
//	value+=val[x];
//	for(int i=0;i<pre[x].size();i++){
//		int u=pre[x][i];
//		if(u==x){
//			num++;
//			if(maxx<value){
//				maxx=value;
//			}
//		}else if(flag[u]==0){
//			dfs(u,num,value);
//		}
//	}
//	return maxx;
//}
int main(){
	freopen("in.txt","r",stdin);
	scanf("%d %d %d %d",&n,&m,&c1,&c2);
	init();
	for(int i=0;i<n;i++){
		scanf("%d",&val[i]);
	}
	for(int i=0;i<m;i++){
		int a,b,c;
		scanf("%d %d %d",&a,&b,&c);
		node tmp;
		tmp.next=a;
		tmp.wei=c;
		adj[b].push_back(tmp);
		tmp.next=b;
		adj[a].push_back(tmp);
	}
	dij(c1);
	int ans=0,maxx=0;
	dfs(c2,ans,maxx);
	printf("%d %d\n",ans,maxx);
	return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值