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.
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; }