牛客寒假5 A-美丽的路径

题目链接
本文参考:https://blog.csdn.net/qq_45778406/article/details/114003999
根据题目和样例可知,边和点都是可以反复使用的,所以可以通过来反复走一条边来改变答案。
我们假设当前我们认定的美丽值为x,用1来表示权值大于等于该美丽值的点,用0表示权值小于美丽值的点。

假设图中存在一条这样的路径

…11…

即有两个权值大于等于美丽值的结点直接连接,那么我们就可以在这两点之间不断来回走动,使得我们当前认定的美丽值不处于K/2+1的位置上,显然美丽值会被往小的方向推动。

假设图中只有如下的路径

…01…

就是两种点不断交替。显然如果我们在这两个点之间来回走动,会在美丽值左边增加一个点,会在美丽值右边增加一个点,美丽值的相对位置并没有发生改变。

…00…

最后一种路径,读者自行考虑

所以我们使用二分的方法来确定当前的美丽值。也就是说根据美丽值来对图进行分类然后遍历。

考虑清楚之后结论如下
1.出现11的情况,说明当前美丽值过小
2.出现00的情况,说明当前美丽值过大
3.出现01的情况

01情况还需要进一步细分
根据起点和终点的选择,有如下四种情况
101010
010101
10101
01010
其中前两种情况在排序之后都是000111,位于k/2+1位置上的为1,说明当前美丽值过小
第三种排序之后为00111,也是过小
第四种排序之后为00011,则是过大

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<string>
#include<map>
#include<unordered_map>
#include<stack>
using namespace std;
typedef  long long ll;
typedef unsigned long long ull;
int t,n,m,st,en,tot,maxx;
int s[200005],head[200005],vis[200005],a[200005];
struct node {
	int v,next;
};
struct node edge[400005];
void init(){
	tot=0;maxx=0;
	for(int i=1;i<=n;i++){
		head[i]=-1;
		s[i]=i;
	}
}
int find(int now){
	if(s[now]==now)return now;
	else return s[now]=find(s[now]);
}
void add(int u,int v){
	edge[tot].v=v;
	edge[tot].next=head[u];
	head[u]=tot;
	tot++;
}
void dfs(int now,int val){
	vis[now]=1;
	for(int i=head[now];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(vis[v])continue;
		if(max(a[now],a[v])>=val)dfs(v,val);//判断是否为01交替,如果为00,则无法到达终点
	}
}
int work(int val){
	for(int i=1;i<=n;i++){// 11判断 
		if(a[i]>=val&&(find(i)==find(st))){
			for(int j=head[i];j!=-1;j=edge[j].next){
				if(a[edge[j].v]>=val)return 1;
			}
		}
	}
	if(a[st]<val&&a[en]<val)return 0;// 首尾均为0判断
	for(int i=1;i<=n;i++)vis[i]=0; 
	dfs(st,val);
	return vis[en];
}
int main(){
	#ifdef LOCAL
	freopen(".\\a.in", "r", stdin);
	#endif
	scanf("%d",&t);
	while(t--){
		scanf("%d%d%d%d",&n,&m,&st,&en);
		init();
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			maxx=max(maxx,a[i]);
		}
		for(int i=1;i<=m;i++){
			int u,v,fu,fv;
			scanf("%d%d",&u,&v);
			add(u,v);add(v,u);
			fu=find(u);fv=find(v);
			if(fu!=fv){
				s[fu]=fv;
			}
		}
		if(find(st)!=find(en)){
			printf("NO\n");continue;
		}
		int l=1;
		int r=1e9+10;
		while(l<r){
			int mid=(l+r+1)/2;
			if(work(mid)==1)l=mid;
			else r=mid-1;
		}
		printf("YES\n%d\n",l);
	}
	return 0;
}
/*
二分美丽值
然后我们将点分成0和1
0表示小于当前美丽值的点
1表示大于等于当前美丽值的点
然后可以很明显的看出如果路径上出现11
也就是两个大于等于的点,然后我们就可以不断地跑这条边,使得当前地美丽值被推走,可见当前美丽值过小 
或者如下三种情况,101010,010101,10101,因为这种情况下,无论怎么寻找路径,位于美丽值位置地永远都为1,所以美丽值同样过小
其它情况则是美丽值过大 

所以我们先特殊判断11的情况,然后再判断首尾都是0的情况,剩下的方案如果是交错的,那么就是美丽值过小的情况 
*/ 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值