19 icpc沈阳网络赛 B. Dudu's maze //并查集+概率

B. Dudu’s maze
题意

给你一个无向无自环图, l y ly ly从一号点出发。
每个点上有糖果或者怪兽,如果遇到糖果他可以捡起糖果,随便走到相邻点。
如果第一次遇到怪物,他可以使用魔法,等概率随机的走向一个相邻点,如果第二次遇到怪兽,游戏结束。
ly知道地图结构,如果他每一步都走的是最佳的,问能得到的糖果数量的最大期望。

思路

这题关键是要想到联通块啊啊啊啊!!!
先把联通的糖果缩成一点。
然后枚举每一个怪兽的位置(要求他的任意一条出边的终点和1在同一联通块,相当于从1到了这个怪兽的位置,之后处理等概率随机走一条边之后就不能再遇到怪兽了,就相当于再加上了一个联通块),取一个最优值。
复杂度是可以保证的,因为最多 2 ∗ n 2*n 2n条边,那么枚举怪兽的出边时,最多 4 ∗ n 4*n 4n次。
并查集老WA,多半是用 i i i而不用 f o u n d ( i ) found(i) found(i) ?

/*   Author : Rshs
 *   Data : 2019-09-19-20.29
 */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 1e9+7;
const int MX = 1e6+5;
int fa[MX];
int found(int x){
    if(x==fa[x])return x;
    return fa[x]=found(fa[x]);
}
vector<int>g[MX];
int q[MX],vis[MX],sa[MX],sb[MX];
bool jud(int x){ //判断是否有出边的终点和1在同一联通快
    for(auto to:g[x]){
        if(found(to)==found(1))return 1;
    }
    return 0;
}
vector<int>v[MX];int cas;
void Main(int avg){
    int n,m,k;cin>>n>>m>>k;
    for(int i=1;i<=n;i++) fa[i]=i,g[i].clear(),v[i].clear(),vis[i]=0;
    for(int i=1;i<=m;i++){
        scanf("%d %d",&sa[i],&sb[i]);
        g[sa[i]].push_back(sb[i]);
        g[sb[i]].push_back(sa[i]);
    }
    for(int i=1;i<=k;i++){
        scanf("%d",&q[i]);vis[q[i]]=1;
    }
    for(int i=1;i<=m;i++){
        if(vis[sa[i]]||vis[sb[i]])continue;
        int aa=found(sa[i]),bb=found(sb[i]);
        if(aa!=bb) fa[aa]=bb;
    }
    for(int i=1;i<=n;i++) if(!vis[i])v[found(i)].push_back(i); //注意怪兽的贡献为0
    int rt=found(1);
    double ans=SZ(v[rt]);
    for(int i=1;i<=k;i++){
        if(!jud(q[i])) continue;
        double sz=SZ(g[q[i]]);
        double now=0;
        for(auto to:g[q[i]]){
            int fto=found(to);
            if(fto==rt) now=now+(double)(SZ(v[rt]))/sz; //回到1的联通块
            else now=now+(double)(SZ(v[fto])+SZ(v[rt]))/sz; //去别的联通块
        }
        ans=max(ans,now);
    }
    printf("%.12lf",ans);
    if(avg!=cas)puts("");
}
int main(){
    cin>>cas;for(int i=1;i<=cas;i++)Main(i);
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值