题目描述
给定一个无向连通图,其节点编号为 1 到 N,其边的权值为非负整数。试求出一条从 1 号节点到 N 号节点的路径,使得该路径上经过的边的权值的“XOR 和”最大。该路径可以重复经过某些节点或边,当一条边在路径中出现多次时,其权值在计算“XOR 和”时也要被重复计算相应多的次数。
直接求解上述问题比较困难,于是你决定使用非完美算法。具体来说,从 1 号节点开始,以相等的概率,随机选择与当前节点相关联的某条边,并沿这条边走到下一个节点,重复这个过程,直到走到 N 号节点为止,便得到一条从 1 号节点到 N 号节点的路径。显然得到每条这样的路径的概率是不同的并且每条这样的路径的“XOR 和”也不一样。现在请你求出该算法得到的路径的“XOR 和”的期望值。
输入输出格式
输入格式:
从文件input.txt中读入数据,输入文件的第一行是用空格隔开的两个正整数N和M,分别表示该图的节点数和边数。紧接着的M行,每行是用空格隔开的三个非负整数u,v和w(1≤u,v≤N,0≤w≤109),表示该图的一条边(u,v),其权值为w。输入的数据保证图连通,30%的数据满足N≤30,100%的数据满足2≤N≤100,M≤10000,但是图中可能有重边或自环。
输出格式:
输出文件 output.txt 仅包含一个实数,表示上述算法得到的路径的“XOR 和”的期望值,要求保留三位小数。(建议使用精度较高的数据类型进行计算)
输入输出样例
输入样例#1:
2 2
1 1 2
1 2 3
输出样例#1:
2.333
说明
样例解释:有1/2的概率直接从1号节点走到2号节点,该路径的“XOR和”为3;有1/4的概率从1号节点走一次1号节点的自环后走到2号节点,该路径的“XOR和”为1;有1/8的概率从1号节点走两次1号节点的自环后走到2号节点,该路径的“XOR和”为3;„„;依此类推,可知“XOR
和”的期望值为:3/2+1/4+3/8+1/16+3/32+„„=7/3,约等于2.333。
分析:很套路啦。
先是对每一位处理(搞位运算的套路)
设f[i]为i-n的期望,有
f[i]+=(1/k)*f[j] (j到i的连边为0)
f[i]+=(1/k)*(1-f[j]) (j到i连边为1)
于是就列出了n-1个方程,注意n号点不能列方程,否则就有自由元啦,直接用f[n]=0就可以了。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
const int maxn=105;
using namespace std;
int n,m,x,y,i,w,ls[maxn],cnt;
double a[maxn][maxn],b[maxn],ans,r[maxn];
struct node{
int y,w,next;
}g[20005];
void build(int k)
{
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++) a[i][j]=0;
}
for (int i=1;i<n;i++)
{
b[i]=0;
for (int j=ls[i];j>0;j=g[j].next)
{
if (g[j].w&(1<<k))
{
a[i][g[j].y]+=(double)-(1/r[i]);
b[i]-=(double)(1/r[i]);
}
else a[i][g[j].y]+=(double)(1/r[i]);
}
a[i][i]-=1;
}
b[n]=0;a[n][n]=1;
}
double guass()
{
for (int i=1;i<=n;i++)
{
for (int j=i+1;j<=n;j++)
{
if (abs(a[i][i])<abs(a[j][i]))
{
for (int k=1;k<=n;k++) swap(a[i][k],a[j][k]);
swap(b[i],b[j]);
}
}
for (int j=1;j<=n;j++)
{
if (i==j) continue;
double rate=a[j][i]/a[i][i];
for (int k=i;k<=n;k++) a[j][k]-=rate*a[i][k];
b[j]-=rate*b[i];
}
}
return b[1]/a[1][1];
}
void add(int x,int y,int w)
{
g[++cnt].y=y;
g[cnt].w=w;
g[cnt].next=ls[x];
ls[x]=cnt;
r[x]++;
}
int main()
{
scanf("%d%d",&n,&m);
for (i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&w);
if (x==y) add(x,y,w);
else add(x,y,w),add(y,x,w);
}
ans=0;
for (i=0;i<30;i++)
{
build(i);
ans+=(double)guass()*(1<<i);
}
printf("%.3lf",ans);
}