zoukankan      html  css  js  c++  java
  • 删边求概率

    WNJXYK和DIDIDI正在玩游戏。 DIDIDI在纸上绘制一个有向图G,该图包含n个点,m个有向边且无循环。 WNJXYK从点1开始。每回合,WNJXYK将随机选择从当前点开始的有向边之一,其可能性均等,然后从该边转到下一个点。游戏将继续,直到没有这种优势为止。 DIDIDI将把宝藏放在点n上,如果WNJXYK经过这一点,他就可以得到宝藏。 WNJXYK有机会删除一条边(他也不能选择删除),这样他可以增加获得宝藏的可能性。您的任务是计算WNJXYK在最佳条件下获得宝藏的可能性。

    输入
    输入的第一行包含一个正整数T,告诉您紧随其后的T个测试用例。
    对于每个测试用例,第一行包含两个整数n,m,分别指示点数,边数。
    然后,以下是m行,每行包含两个整数x和y,表示存在从x到y的边。
    保证不存在多个边缘。
    输出
    对于每个测试用例,打印一行“ Case #x:y”,其中x是案例编号(从1开始),y是他得到宝藏的概率。 (四舍五入到小数点后六位)。
    样例输入

    2
    4 4
    1 2
    1 3
    1 4
    2 3
    4 5
    1 2
    1 3
    1 4
    2 3
    2 4
    

    样例输出 Copy

    Case #1: 0.500000
    Case #2: 0.750000
    

    提示

    Tips:1≤T≤100,3≤n≤50,1≤m≤n(n-1)/2
    Case 1: delete 1 - 2, 50% 1->3, 50% 1->4.
    Case 2: delete 1 - 3, 25% 1->2->4, 25% 1->2->3, 50% 1->4.
     

    反向建图,枚举删除每一条边。

    图中不存在环,因此到达每一个点的值都是由其父节点等概率分配来的,于是我们可以统计每个点有多少个孩子,然后进行记忆化搜索。

    #pragma GCC optimize(1)
    #pragma GCC optimize(2)
    #pragma GCC optimize(3,"Ofast","inline")
    #include<cstring>
    #include<cstdio>
    #include<iostream>
    #include<queue> 
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    template <typename Tp>
    void read(Tp &x){//read(n);
        x=0;char ch=1;int fh;
        while(ch!='-'&&(ch>'9'||ch<'0')){
            ch=getchar();
        }
        if(ch=='-'){
            fh=-1;ch=getchar();
        }else fh=1;
        while(ch>='0'&&ch<='9'){
            x=(x<<1)+(x<<3)+ch-'0';ch=getchar();
        }
        x*=fh;
    }
    inline char read1()//字符串读入挂
    {
        register char ch=getchar();
        while(ch<'A'||ch>'M')ch=getchar();
        return ch; 
    }
    const int maxn=200;
    const int mod=1000000007;
    const int INF=0x3f3f3f;
    struct node{
        int to;
        int next;
    }edge[maxn*maxn];
    int n,m;
    int dx,dy,tot;
    double v[maxn],ans;
    int head[maxn],out[maxn]; 
    int vis[maxn][maxn];
    void add(int u,int v){
        edge[tot].to=v;
        edge[tot].next=head[u];
        head[u]=tot++; 
    }
    double dfs(int x){
        double ans1=0.0;
        if(x==1){
            ans1=1.0;
        }
        else{
            if(fabs(v[x])>1e-7){//记忆化搜索 
                return v[x];
            }
            for(int i=head[x];~i;i=edge[i].next){
                int to=edge[i].to;
                if(to==dx&&x==dy){
                    continue;
                }
                ans1+=dfs(to)/out[to];
            }
        }
        v[x]=ans1;
        return ans1;
    }
    void inint(){
        memset(v,0.0,sizeof(v));
        memset(head,-1,sizeof(head));
        memset(out,0,sizeof(out));
        memset(vis,0,sizeof(vis));
    }
    int main(){
        int t;
        cin>>t;
        for(int Case=1;Case<=t;Case++){
            inint();
            ans=0.0; 
            read(n),read(m);
            tot=0;
            int u,vv;
            for(int i=0;i<m;i++){
                read(u),read(vv);
                add(vv,u);//反向建边 
                vis[u][vv]=1;
                out[u]++;
            }
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    if(vis[i][j]){//枚举删那个边 
                        dx=i,dy=j;
                        memset(v,0.0,sizeof(v));
                        out[i]--;
                        ans=max(ans,dfs(n));//从n开始遍历 
                        out[i]++;            
                    } 
                }
            } 
            memset(v,0.0,sizeof(v)); 
            dx=dy=-1;//不删边 
            ans=max(ans,dfs(n));
            printf("Case #%d: %.6lf
    ",Case,ans);
        }
        return 0;
    }
  • 相关阅读:
    狡猾的商人
    差分约束系统
    【模板】负环
    关于Java8的精心总结
    rabbitmq+sleuth+zinkip 分布式链路追踪
    Linux下一只五颜六色的「猫」
    整理 Linux下列出目录内容的命令
    从封装变化的角度看设计模式——组件协作
    从封装变化的角度看设计模式——接口隔离
    从封装变化的角度看设计模式——对象创建
  • 原文地址:https://www.cnblogs.com/lipu123/p/13771616.html
Copyright © 2011-2022 走看看