• xor和路径(codevs 2412)


    题目描述 Description

    给定一个无向连通图,其节点编号为1到N,其边的权值为非负整数。试求出一条从1号节点到 N 号节点的路径,使得该路径上经过的边的权值的“XOR 和”最大。该路径可以重复经过某些节点或边,当一条边在路径中出现多次时,其权值在计算“XOR 和”时也要被重复计算相应多的次数。

    直接求解上述问题比较困难,于是你决定使用非完美算法。具体来说,从1号节点开始,以相等的概率,随机选择与当前节点相关联的某条边,并沿这条边走到下一个节点,重复这个过程,直到走到N号节点为止,便得到一条从1号节点到N号节点的路径。显然得到每条这样的路径的概率是不同的并且每条这样的路径的“XOR 和”也不一样。现在请你求出该算法得到的路径的“XOR和”的期望值。 

    输入描述 Input Description

    第一行是用空格隔开的两个正整数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 Description

    仅包含一个实数,表示上述算法得到的路径的“XOR和”的期望值,要求保留三位小数。(建议使用精度较高的数据类型进行计算) 

    样例输入 Sample Input

    2 2
    1 1 2
    1 2 3

    样例输出 Sample Output

    2.333

    数据范围及提示 Data Size & Hint

    样例解释:有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[x]为从x到n的异或期望。 
        f[x]=Σ(1/d[x])*f[y](边权为0)+Σ(1/d[x])*(1-f[y])(边权为1)
        将上式变形得到:
        f[x]-Σ(1/d[x])*f[y](边权为0)+Σ(1/d[x])*f[y](边权为1)=Σ (1/d[x])(边权为1)
        然后高斯消元。 
    */
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #define N 110
    #define M 10010
    #define ld long double
    using namespace std;
    int head[N],d[N],cnt,n,m;
    ld a[N][N],ans;
    struct node{int v,w,pre;}e[M*2];
    void add(int u,int v,int w){
        e[++cnt].v=v;e[cnt].w=w;e[cnt].pre=head[u];head[u]=cnt;
    }
    void gauss(){
        for(int i=1;i<=n;i++){
            int id=i;ld maxn=fabs(a[i][i]);
            for(int j=i+1;j<=n;j++) if(fabs(a[j][i])>maxn) id=j,fabs(a[j][i]);
            if(id!=i) swap(a[i],a[id]);
            ld t=a[i][i];
            for(int j=1;j<=n+1;j++) a[i][j]/=t;
            for(int j=1;j<=n;j++)
                if(j!=i){
                    ld t=a[j][i];
                    for(int k=1;k<=n+1;k++)
                        a[j][k]-=t*a[i][k];
                }
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            int u,v,w;scanf("%d%d%d",&u,&v,&w);add(u,v,w);d[u]++;
            if(u!=v) add(v,u,w),d[v]++;
        }
        for(int t=0;t<=30;t++){
            memset(a,0,sizeof(a));
            for(int i=1;i<n;i++){
                a[i][i]=1.0;
                for(int j=head[i];j;j=e[j].pre){
                    if(e[j].w&(1<<t)) a[i][e[j].v]+=1.0/d[i],a[i][n+1]+=1.0/d[i];
                    else a[i][e[j].v]-=1.0/d[i];
                }
            }
            a[n][n]=1.0;gauss();ans+=a[1][n+1]*(1<<t);
        }
        printf("%.3lf",(double)ans);
        return 0;
    }
  • 相关阅读:
    Windows服务BAT命令-安装、卸载、启动、停止
    身份认证
    密钥管理概述
    快速上手几个Linux命令
    递归
    数字签名的实现方案
    数字签名
    密码学基础
    你可以把Linux内核当成一家软件外包公司的老板
    数学归纳法
  • 原文地址:https://www.cnblogs.com/harden/p/6602469.html
Copyright © 2020-2023  润新知