【CF113D】Museum 学习笔记

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

posted @ 2022-05-10 09:52  千叶繁华  阅读(34)  评论(0编辑  收藏  举报