SCU - 4444 Travel

本文针对特定图论问题,提出了一种通过分类讨论的方式解决最优路径寻找问题的方法。该方法根据边权的不同分为两种情况进行讨论,并给出了具体的实现代码。

题面在这里!

 

    暴力分类讨论233333.

 

    肯定要先分 a<=b 和 a>b 两种情况讨论啊QWQ

 

    对于第一种情况,肯定是优先走a的边。

    如果1和n有边的话,那么答案直接就是a;

    否则,答案就是 b 和 只走a到n的最短路 中的 更小值。

 

    对于第二种情况,肯定是优先走b的边。

    如果1和n有边的话,那么答案肯定直接就是b;

    否则,如果所有2~n-1的点都要么和1有a边要么和n有a边,那么答案就是a (因为不管怎么走都要经过a边)。

    否则答案就是 min(2*b,a) ,分别代表走一个既不和1有边又不和n有边的点中转,或者直接从1走到n。

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+5;

inline int read(){
	int x=0; char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
	return x;
}

int hd[N],ne[N*10],to[N*10],A,B,d[N],n,m,num;
ll ans;

inline void add(int x,int y){ to[++num]=y,ne[num]=hd[x],hd[x]=num;}

inline void bfs(){
	memset(d,0,sizeof(d));
	queue<int> q; int x;
	d[1]=1,q.push(1);
	
	while(!q.empty()){
		x=q.front(),q.pop();
		for(int i=hd[x];i;i=ne[i]) if(!d[to[i]]){
			d[to[i]]=d[x]+1,q.push(to[i]);
		}
	}
}

inline bool ck1(){
	for(int i=hd[1];i;i=ne[i]) if(to[i]==n) return 1;
	return 0;
} 

inline bool ck2(){
	for(int i=2,now;i<n;i++){
		now=0;
	    for(int j=hd[i];j;j=ne[j]) if(to[j]==1||to[j]==n){ now=1; break;}
	    if(!now) return 1;
	}
	return 0;
} 

int main(){
	
	while(scanf("%d%d%d%d",&n,&m,&A,&B)==4){
	    fill(hd+1,hd+n+1,0),num=0,ans=1e18;
	    
	    for(int i=1,uu,vv;i<=m;i++){
	    	uu=read(),vv=read();
	    	add(uu,vv),add(vv,uu);
		}
		
		if(A<=B){
			ans=B,bfs();
			if(d[n]) ans=min(ans,(d[n]-1)*(ll)A);
		}
		else{
			if(!ck1()) ans=B;
			// 1和n 在A图中不连通 
			else if(!ck2()) ans=A;
			// 没有一个点 和1和n相连的边都是B边 
			// (也就是任意一条1到n的路径都至少要经过一条A边) 
			else ans=min(B<<1,A);
			// 可以找到一个中转点使得它到1和n都是B边 
		}
		
		printf("%lld\n",ans);
	}
	
	return 0;
}

 

转载于:https://www.cnblogs.com/JYYHH/p/9265147.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值