专栏首页数据结构与算法BZOJ3143: [Hnoi2013]游走(期望DP 高斯消元)

BZOJ3143: [Hnoi2013]游走(期望DP 高斯消元)

Description

一个无向连通图,顶点从1编号到N,边从1编号到M。  小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。  现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。

Input

第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1≤u,v≤N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N≤10,100%的数据满足2≤N≤500且是一个无向简单连通图。

Output

仅包含一个实数,表示最小的期望值,保留3位小数。

Sample Input

3 3 2 3 1 2 1 3

Sample Output

3.333

HINT

边(1,2)编号为1,边(1,3)编号2,边(2,3)编号为3。

Source

非官方数据

这题真TM恶心啊。。

思路大概是先表示出边的概率,然后表示出点的概率

发现点的概率不能直接搞

然后高斯消元搞一搞

最后贪心的加边,显然概率越小的编号应该越大

详细一点的题解在这里

https://www.luogu.org/problemnew/solution/P3232

#include<cstdio>
#include<cstring>
#include<algorithm>
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int MAXN=1e6+10;
const double eps=1e-7;
char buf[1<<23],*p1=buf,*p2=buf;
inline int read()
{
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int N,M;
struct node
{
    int u,v,nxt;
}edge[MAXN];
int head[MAXN],num=1;
inline void AddEdge(int x,int y)
{
    edge[num].u=x;
    edge[num].v=y;
    edge[num].nxt=head[x];
    head[x]=num++;
}
double f[1001][1001],ans[MAXN],E[MAXN],inder[MAXN];
int S[MAXN],T[MAXN];
int dcmp(double x)
{
    if(x<eps&&x>-eps) return 0;
    else return x<0?-1:1;
}
void Gauss()
{

    for(int i=1;i<N;i++)
    {
        int mx=i;
        for(int j=i+1;j<N;j++)
            if( dcmp(f[j][i]-f[mx][i])>0 ) mx=j;
        if(mx!=i) swap(f[i],f[mx]);
        for(int j=i+1;j<N;j++)
        {
            double tmp=f[j][i]/f[i][i];
            for(int k=i;k<=N;k++)
                f[j][k]-=(double)tmp*f[i][k];
        }
    }
    for(int i=N-1;i>=1;i--)
    {
        for(int j=i+1;j<N;j++)
            f[i][N]-=ans[j]*f[i][j];
        ans[i]=f[i][N]/f[i][i];
    }
}
int main()
{
    #ifdef WIN32
    freopen("a.in","r",stdin);
    #else
    #endif
    memset(head,-1,sizeof(head));
    N=read(),M=read();
    for(int i=1;i<=M;i++)
    {
        int x=read(),y=read();
        AddEdge(x,y);AddEdge(y,x);
        inder[x]++;inder[y]++;
        S[i]=x;T[i]=y;
    }
    f[1][N]=1;
    for(int i=1;i<N;i++) f[i][i]=1;
    for(int i=1;i<N;i++)
        for(int j=head[i];j!=-1;j=edge[j].nxt)
            if(edge[j].v!=N)
                f[i][edge[j].v]=(double)-1.00/inder[edge[j].v];
    Gauss();
    for(int i=1;i<=M;i++) 
        E[i]=ans[S[i]]/inder[S[i]]+ans[T[i]]/inder[T[i]];
    sort(E+1,E+M+1);
    double out=0;
    for(int i=1;i<=M;i++)
        out+=E[i]*(M-i+1);
    printf("%.3lf",out);
    return 0;
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 2017.5.20欢(bei)乐(ju)赛解题报告

    预计分数:100+20+50=first 实际分数:20+0+10=gg 水灾(sliker.cpp/c/pas) 1000MS  64MB 大雨应经下了几天雨...

    attack
  • 树上莫队算法

    attack
  • BZOJ1023: [SHOI2008]cactus仙人掌图(仙人掌dp)

      如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人掌 图(cactus)。所谓简单回路就是指在图上不...

    attack
  • LeetCode 323. 无向图中连通分量的数目(并查集)

    给定编号从 0 到 n-1 的 n 个节点和一个无向边列表(每条边都是一对节点),请编写一个函数来计算无向图中连通分量的数目。

    Michael阿明
  • 2017.5.20欢(bei)乐(ju)赛解题报告

    预计分数:100+20+50=first 实际分数:20+0+10=gg 水灾(sliker.cpp/c/pas) 1000MS  64MB 大雨应经下了几天雨...

    attack
  • OpenCV图像处理专栏十 | 利用中值滤波进行去雾

    这是OpenCV图像处理专栏的第十篇文章,介绍一种利用中值滤波来实现去雾的算法。这个方法发表于国内的一篇论文,链接我放附录了。

    BBuf
  • 树上莫队算法

    attack
  • 挑战程序竞赛系列(23):3.2折半枚举

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.n...

    用户1147447
  • 2017广东工业大学程序设计竞赛决赛 题解&源码(A,数学解方程,B,贪心博弈,C,递归,D,水,E,贪心,面试题,F,贪心,枚举,LCA,G,dp,记忆化搜索,H,思维题)

    心得: 这比赛真的是不要不要的,pending了一下午,也不知道对错,直接做过去就是了,也没有管太多! Problem A: 两只老虎 Description ...

    Angel_Kitty
  • 浙大版《C语言程序设计(第3版)》题目集 习题5-4 使用函数求素数和

    其中函数prime当用户传入参数p为素数时返回1,否则返回0;函数PrimeSum返回区间[m, n]内所有素数的和。题目保证用户传入的参数m≤n。

    C you again 的博客

扫码关注云+社区

领取腾讯云代金券