bzoj3143 [Hnoi2013]游走
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3143
题意:
一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
数据范围
2≤N≤500且是一个无向简单连通图。
题解:
这里提供了一种用高斯消元解方程来解决有后效性问题的思路。
我们要得到每条边的期望经过次数,然后把期望最大的排名尽量小
把求没条边的期望经过次数转化为每个点的期望经过次数,
则 令 l[i]为一条边的期望经过次数,f[u]为一个点的期望经过次数,
那么: l[i]=f[u]* (1/degree(u))+f[v]*(1/degree(v))
特殊地,对于一个端点为n的点,要忽略点n,因为不可能从点n过来
DP方程应该很容易得到:
f[u]=∑( f[v]*(1/degree(v))) (v与u有边)
特殊地
f[1]=∑( f[v]*(1/degree(v))) +1 (1本来经过了一次)
对于与n有边的点 f[u]=∑( f[v]*(1/degree(v))) (v!=n, u与点n有边)
f[n]用不到。
发现有后效性,这个DP不了诶。
但是我们呢得到了一个 关于 f[i]的,(n-1)元一次方程组。
于是 高斯消元解方程,得到f[i]求l[i],得到答案。
发现自己的高消快忘了……
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m;
const int N=505;
const double EPS=1e-10;
int head[N],to[2*N*N],nxt[2*N*N],du[N],num=0;
int uu[N*N],vv[N*N];
double a[N][N],f[N],l[2*N*N];
void build(int u,int v)
{
num++;
to[num]=v;
nxt[num]=head[u];
head[u]=num;
}
void gauss()
{
int cnt=0;
for(int i=1;i<=n;i++)
{
int j=-1; double mx=EPS;
for(int k=cnt+1;k<=n;k++)
{
if(fabs(a[k][cnt+1])>mx)
{
mx=fabs(a[k][cnt+1]); j=k;
}
}
if(j==-1) continue;
for(int k=1;k<=n+1;k++)
swap(a[cnt+1][k],a[j][k]);
for(int j=1;j<=n;j++)
{
if(j==cnt+1) continue;
if(fabs(a[j][i])<EPS) continue;
double r=a[j][i]/a[cnt+1][i];
for(int k=1;k<=n+1;k++)
{
a[j][k]-=a[cnt+1][k]*r;
}
}
cnt++;
}
for(int i=1;i<=n;i++)
{
f[i]=a[i][n+1]/a[i][i];
}
}
bool cmp(const double &A,const double &B)
{
return A-B>EPS;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<=n+1;i++)
for(int j=0;j<=n+1;j++)
a[i][j]=0.0;
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
uu[i]=u; vv[i]=v;
build(u,v); build(v,u);
du[u]++; du[v]++;
}
n--;
for(int i=1;i<=n;i++)
{
a[i][i]=-1.0;
if(i==1) a[i][n+1]=-1.0;
for(int j=head[i];j;j=nxt[j])
{
int v=to[j];
if(v==n+1) continue;
a[i][v]=(double)1.0/du[v];
}
}
gauss();
n++;
for(int i=1;i<=m;i++)
{
l[i]=(double)f[uu[i]]/du[uu[i]]+(double)f[vv[i]]/du[vv[i]];
}
sort(l+1,l+m+1,cmp);
double ans=0;
for(int i=1;i<=m;i++)
ans+=(double)l[i]*i;
printf("%0.3lf\n",ans);
return 0;
}

本文介绍了解决BZOJ3143问题的一种方法,通过使用高斯消元法来确定边的编号顺序,使得随机游走在图上的总分期望值达到最小。文章详细阐述了如何建立数学模型,并给出了具体的实现代码。
593

被折叠的 条评论
为什么被折叠?



