2021牛客寒假算法基础集训营5 A - 美丽的路径( 二分(中位数) + dfs )

链接: A - 美丽的路径
题意:
在这里插入图片描述
思路:

  1. 我们可以二分这个美丽值 , 对于大于等于美丽值的值权值设置为 1,否则为 0 .
  2. 如果在 联通块中 某条边两端的点权值都是 1 ,那么这个美丽值一定是合法的(我们可以在这条边反复走 无数次,最后的中位数一定会是两端点中的一个且会大于美丽值)。
  3. 如果不存在这样的相邻的 1 ,那我们就要找到一条路径 1的个数比 0 多,因为已经不存在相邻的 1 了,那唯一合法的解一定是 101010 …1 s 和 t 都是 1,中间部分 01 交替。至于其他情况都是不合法的。
  4. 怎么找找这条合法路径 , 我们可以先以 s 为起点 跑一次 dfs 把权值为 1 或者 上一个节点权值为 1 的节点标记为可以走 , 再跑一次 dfs (只走可以走的点),如果可以到达 t ,且 s 和 t 权值都为 1 ,那就说明可行。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ioss ios::sync_with_stdio(false);cout.tie(NULL)
const int maxn=1e6 + 7;
const int mod = 998244353;
const int rr = 233;
int T,n,m,s,t;
int num,head[maxn],flag,ma;
int val[maxn],vis[maxn],a[maxn],b[maxn];
struct node{
    int v,next;
}e[maxn << 1];
void add(int u,int v){
    e[num].v = v;
    e[num].next = head[u];
    head[u] = num ++;
}
void dfs1(int u){
    for(int i = head[u]; i != -1; i = e[i].next){
        int to = e[i].v;
        if(a[u] && a[to]) flag = 1;        // 两个 1 答案可行
        if(a[u] || a[to]) b[to] = 1;       // 代表这个点在下一个 dfs 可以走 ,保证是 01 交替
        if(vis[to] == 0){
            vis[to] = 1;
            dfs1(to);
        }
    }
}
void dfs2(int u){
    for(int i = head[u]; i != -1; i = e[i].next){
        int to = e[i].v;
        if(vis[to] == 0 && b[to]){
            vis[to] = 1;
            dfs2(to);
        }
    }
}
int check(int m){
    for(int i = 1; i <= n; i ++){
        vis[i] = 0;b[i] = 0;
        if(val[i] >= m) a[i] = 1;
        else a[i] = 0;
    }
    vis[s] = 1;
    flag = 0;
    dfs1(s);
    if(flag) return 1;
    if(a[s] == 0 || a[t] == 0) return 0;  // 有一个不为 1 就不行
    for(int i = 1; i <= n; i ++){
        vis[i] = 0;
    }
    vis[s] = 1;
    dfs2(s);
    if(vis[t]) return 1;                  // 可以到达 可行
    return 0;

}
int main (){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d%d",&n,&m,&s,&t);
        num = 0,ma = 0;
        for(int i = 1; i <= n; i ++) head[i] = -1,vis[i] = 0;
        for(int i = 1;i <= n; i ++){
            scanf("%d",&val[i]);
            ma = max(ma,val[i]);             
        }
        for(int i = 1,u,v; i <= m; i ++){
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        vis[s] = 1;
        dfs1(s);
        if(vis[t] == 0){          // 判断是否联通
            printf ("NO\n");
            continue;
        }
        int l = 1,r = ma,mid,ans = -1;
        while(l <= r){
            mid = (l + r) / 2;
            if(check(mid)){
                ans = mid;
                l = mid + 1;
            }
            else r = mid - 1;
        }
        if(ans == -1) printf ("NO\n");
        else {
            printf ("YES\n");
            printf ("%d\n",ans);
        }
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值