• BZOJ 4899 记忆的轮廓


    话说BZOJ 是不是死了啊

    (已经没有传送门了)

    设 $f[i][j]$ 表示走到第 $j$ 个位置确定了 $i$ 个存档点时的最小代价,并强制第 $j$ 个位置有一个存档点

    那么设 $cst[i][j]$ 表示存档点在 $i$ ,走到位置 $j$ 的代价, $f$ 有转移:

    $f[i][j]=f[i-1][k]+cst[k][j]$

    考虑 $cst[i][j]$ 如何计算,对每个起点 $i$ 求出 $i$ 指向的错误节点 $v$ 重新走回 $i$ 的期望步数 $dis[v]$,这个可以简单树形 $dp$ 得到

    那么设 $val=sum_{v in son(j-1)} (cst[i][j-1]+1+dis[v])$ ,那么有 $cst[i][j]=cst[i][j-1]+1+val$

    这里 $val$ 意义显然为从 $j-1$ 走到 $j$ 的期望代价,因为从 $j-1$ 到各个后继的概率一样,所以期望就是把每个后继各走一遍

    加上 $cst[i][j-1]+1$ 显然是因为存档点在 $i$ ,要走到 $j-1$ 的后面一个儿子代价就是 $cst[i][j-1]+1$

    然后这样就可以 $n^3$ 跑 $dp$ 了,考虑如何优化

    把 $cst$ 的表打出来发现 $cst[i][j]$ 比 $cst[i][j-1]$ 的值大很多,发现 $cst$ 增长很快,显然超过一次函数

    那么对于 $a<b<c<d$ ,就有 $cst[a][c]+cst[b][d]<=cst[a][d]+cst[b][c]$,即满足四边形不等式

    所以对于 $f[i][j]$ 的最优转移点 $k$ ,$f[i][j+1]$ 的最优转移点一定不小于 $k$(不然我们直接从 $f[i-1][k]$ 转移会更优,写写式子就知道了)

    然后用决策单调性分治优化即可

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    using namespace std;
    typedef long long ll;
    typedef double db;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2007;
    const db INF=1e99;
    int T,n,m,p;
    vector <int> V[N];
    db f[N][N],cst[N][N],dis[N];
    void dfs(int x,int fa)
    {
        int sz=V[x].size();
        if(sz==1) { dis[x]=1; return; }
        dis[x]=0;
        for(int i=0;i<sz;i++)
        {
            int &v=V[x][i]; if(v==fa) continue;
            dfs(v,x); dis[x]+=dis[v]+1;
        }
        dis[x]/=sz-1;
    }
    void solve(int i,int l,int r,int ql,int qr)
    {
        int mid=l+r>>1,pos=ql,R=min(qr,mid-1); f[i][mid]=INF;
        for(int k=ql;k<=R;k++)
        {
            db val=f[i-1][k]+cst[k][mid];
            if(val<f[i][mid]) f[i][mid]=val,pos=k;
        }
        if(l<mid) solve(i,l,mid-1,ql,pos);
        if(mid<r) solve(i,mid+1,r,pos,qr);
    }
    int main()
    {
        T=read();
        while(T--)
        {
            n=read(),m=read(),p=read();
            for(int i=1;i<=m-n;i++)
            {
                int a=read(),b=read();
                V[a].push_back(b); V[b].push_back(a);
            }
            for(int i=1;i<=n;i++)
                for(auto A: V[i]) dfs(A,i);
            for(int i=1;i<=n;i++)
            {
                cst[i][i]=0;
                for(int j=i+1;j<=n;j++)
                {
                    cst[i][j]=cst[i][j-1]+1;
                    for(auto A: V[j-1])
                        cst[i][j]+=cst[i][j-1]+1 + dis[A];
                }
            }
            f[1][1]=0; for(int i=2;i<=n;i++) f[1][i]=INF;
            for(int i=2;i<=p;i++)
                solve(i,1,n,1,n);
            printf("%.4lf
    ",f[p][n]);
            for(int i=1;i<=m;i++) V[i].clear();
        }
        return 0;
    }
  • 相关阅读:
    BigInteger实现除法取余
    BigDecimal介绍及BigDecimal实现四舍五入
    Jenkins的凭证管理
    Adapter(适配器)-类对象结构型模式
    singleton(单件)-对象创建型模式
    ProtoType(原型)-对象创建型模式
    Factory Method(工厂方法)-对象创建型模式
    Builder(生成器)-对象创建型模式
    Abstract Factory(抽象工厂)模式
    设计模式-序
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11692720.html
Copyright © 2020-2023  润新知