题目描述
这个世界是一个有向图,图中有n个点m条边且无重边无自环,每秒第i条边出现的概率是p[i]/100,一开始Samjia在1点,每一秒假设Samjia在点x上,那么Samjia要从存在的边中选一条来走,不可以不走,如果不存在可以走的边,那么Samjia就会掉出这个世界,假设Samjia绝顶聪明,问最后Samjia可以成功到达n的概率是多少。精度是1e-6
对于40%的数据,2<=n<=8
对于70%的数据,2<=n<=20
对于100%的数据,2<=n<=50,0<=m<=n*(n-1),0<=p[i]<=100
题目分析
这道题看起来挺麻烦的,一开始看到有环就觉得很麻烦。
不妨设P[x]表示在x点到点n的概率,如果我们知道了它所有出边(x,y)的P[y],我们要怎么采取最优策略呢?我们来考虑一下所有情况,假设一种情况出现了若干条边,连向a[1..k],那么我们肯定是走P[a[i]]最大的一个a[i],不然就不优了。那么我们可以把后继按P从大到小排序,每次计算出P比他大的后继的边都不出现,而他出现的概率,就可以算出P[x]了。
问题是有可能有环,有些后继的P是无法确定的。
这个时候眼前摆了两条路。
逼近精度
你设个步数,比如10000。然后超过这个步数的路径长度概率都视为0。具体的,设f[i][j]表示走了i长度,走到j,它到n的概率。那么能够水一些分。事实证明,这场比赛worldwide_D这样写过了。
调整法
有个暴力,就是枚举所有P的相对大小的情况,然后取P[1]最大的那种作为答案。
你随机定一个顺序,然后按这个顺序能够确定P之间的关系,然后高斯消元。
由于你的顺序可能是错误的,你的P[1]可能会算出来比原来小,其他P可能也不一样。
这个时候,算出来的情况会和你定的顺序不一样,你可以取这个顺序作为新的顺序再继续做。
就是一个迭代过程,不断调整,直到一样,那么这是正确的答案了。
具体证明需要用到比较高深的知识,感受一下就好~反正顺序相同很显然是答案正确的必要条件,至于为什么这样做事对的就…以后会知道的~
不断调整,一个x的相对排名最多会变n次,那么最坏调整复杂度O(n^2),高斯消元O(n^3),总复杂度O(n^5)。实际上很难调整这么多次。
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
const int N=2500+5,mo=1e9+7,M=55;
const db eps=1e-6;
db a[M][M],E[M],xs;
int rk[M],rrk[M],sa[M],mx,i,j,k,n,m,x,p,y,z,pp;
int tt,b[N],nxt[N],fst[N],c[N],d[N];
void cr(int x,int y,int z)
{
tt++;
b[tt]=y;
c[tt]=z;
nxt[tt]=fst[x];
fst[x]=tt;
}
bool cmp(int x,int y)
{
return rk[b[x]]<rk[b[y]];
}
bool cmp2(int x,int y)
{
return E[x]>E[y];
}
void geteq()
{
fo(i,1,n) fo(j,1,n+1) a[i][j]=0;
fo(x,1,n-1)
{
d[0]=0;
for(p=fst[x];p;p=nxt[p])
d[++d[0]]=p;
sort(d+1,d+1+d[0],cmp);
a[x][x]=-1;
xs=1;
fo(i,1,d[0])
{
p=d[i];
a[x][b[p]]=xs*db(c[p])/100.0;
xs*=1.0-db(c[p])/100.0;
}
}
a[n][n]=1;
a[n][n+1]=1;
}
void gauss()
{
fo(i,1,n)
{
mx=i;
fo(j,i+1,n) if (fabs(a[j][i])>fabs(a[mx][i])) mx=j;
fo(j,1,n+1) swap(a[i][j],a[mx][j]);
if (fabs(a[i][i])<eps) continue;
fo(j,i+1,n)
{
xs=a[j][i]/a[i][i];
fo(k,i,n+1) a[j][k]-=a[i][k]*xs;
}
}
fd(i,n,1)
{
if (fabs(a[i][i])<eps) continue;
fd(j,i-1,1)
{
xs=a[j][i]/a[i][i];
fo(k,j,n+1) a[j][k]-=a[i][k]*xs;
}
}
fo(i,1,n)
E[i]=a[i][n+1]/a[i][i];
fo(i,1,n) sa[i]=i;
sort(sa+1,sa+1+n,cmp2);
fo(i,1,n) rrk[sa[i]]=i;
}
int main()
{
freopen("t1.in","r",stdin);
//freopen("graph.out","w",stdout);
scanf("%d %d\n",&n,&m);
srand(n*m-n+m);
fo(i,1,m)
{
scanf("%d %d %d",&x,&y,&z);
cr(x,y,z);
}
fo(i,1,n) rk[i]=i;
random_shuffle(rk+1,rk+1+n);
do
{
geteq();
gauss();
pp=1;
fo(i,1,n) if (rrk[i]!=rk[i]) pp=0;
fo(i,1,n) rk[i]=rrk[i];
}while (!pp);
printf("%.6lf\n",E[1]);
}