【CF113D】Museum 学习笔记
杂技:
https://szmssf.blog.luogu.org/solution-cf113d
由于题目允许小误差,可求近似解,但要会调参。
正解:
https://blog.csdn.net/luositing/article/details/111277601
启示:
概率和期望关系紧密,尤其是结果只有0、1时,期望基本就是概率。
通过枚举来分类/制造条件,给复杂不知求法的答案一个基于枚举意义的求法,配合后继优化,也是个思路。同时能就解决部分后效性问题。
列方程组解决复杂的后效性
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <vector>
using namespace std;
vector<int> e[25];
double p[25],a[505][505];
int n,m,sa,sb;
inline int id(int x,int y)
{
if (x==y) return n*n-n+x;
return (x-1)*(n-1)+y-(y>x);//把所有项“线性化”展开到一维。让项个数内的编号都对应上一项。*
//这里考虑特殊项放最后,那么每行的普通项有n-1个,再考虑拿走特殊项后对普通项编号的影响。要保证编号不重不漏。*
}
void gauss(int n,int m)
{
for (int i=1;i<=n;i++)
{
int pos=i;
for (int j=i+1;j<=n;j++) if (fabs(a[j][i])>fabs(a[pos][i])) pos=j;
if (pos>i) swap(a[i],a[pos]);
for (int j=1;j<=n;j++)
if (j!=i)
{
double t=a[j][i]/a[i][i];//只需保证消好后范围内斜线外都为0即可。减少实数运算次数,减少误差*
for (int k=i;k<=m;k++)
a[j][k]-=t*a[i][k];
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&sa,&sb);
if (sa==sb)
{
for (int i=1;i<=n;i++)
if (i==sa) printf("%.10f ",1.0);
else printf("%.10f ",0.0);
return 0;
}
for (int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v),e[v].push_back(u);
}
for (int i=1;i<=n;i++) scanf("%lf",&p[i]);
for (int u=1;u<=n;u++)//每行一个方程,对应列为对应项的系数。 *
for (int v=1;v<=n;v++)
if (u!=v)
{
int s=id(u,v);
double pu=1.0/e[u].size(),pv=1.0/e[v].size();
for (int i=0;i<=(int)e[u].size();i++)
for (int j=0;j<=(int)e[v].size();j++)
{
double t=(i<(int)e[u].size()? (1-p[u])*pu:p[u])*(j<(e[v].size())? (1-p[v])*pv:p[v]);
a[s][id(i<(int)e[u].size()? e[u][i]:u,j<(int)e[v].size()? e[v][j]:v)]=t;//i=size时即为自身
}
a[s][s]-=1;//“x=……”的等号右边记完了,别忘了等号左边。 *
}
gauss(n*n-n,n*n);
int s=id(sa,sb);
for (int i=n*n-n+1;i<=n*n;i++) printf("%.10f ",-a[s][i]/a[s][s]);
return 0;
}