删边求概率

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\n",Case,ans);
    }
    return 0;
}

 

posted @ 2020-10-05 21:11  哎呦哎(iui)  阅读(111)  评论(0编辑  收藏  举报