• 【BZOJ4609】Branch Assignment(WF2016)-最短路+决策单调性优化DP


    测试地址:Branch Assignment
    题目大意:有一张n个点的有向强连通图,其中b个点是部门,另有一个点是总部,要把部门分成s个组,每组之内每两个部门都要相互传递信息,一个部门向另一个部门传递信息,需要从这个部门走到总部,再从总部走到另一个部门,问总的传递距离的最小值。
    做法:本题需要用到最短路+决策单调性优化DP。
    首先,我们显然要用两次SPFA求出所有点到总部,以及总部到所有点的距离。然后对于一个有k个部门的组,每个组要到总部k1次,又要从总部回来k1次,所以这一组的贡献就是(k1)×v(dis(v,b+1)+dis(b+1,v))(因为总部是b+1号)。
    dis(v,b+1)+dis(b+1,v)为每个点的点权,我们能得到如下的结论:一定存在一种最优方案,使得选出的所有的组在点按点权排序的顺序中,是一段连续的区间。其实挺好证明,对于每一种可能的分组方案(这里的“分组方案”指每个组部门数的分配,先不考虑具体部门),我们实际上就是给每个点附上一个系数,使得系数乘上点权之和最小。根据排序不等式,在点权从小到大排列时,系数也应该从小到大排列最好。这样我们不仅得出了上面的结论,还能得到一个结论:最优方案中,后面的组总比前面的更大(因为系数是组的大小1)。
    于是我们能很快写出一个O(n3)状态转移方程:
    f(i,j)为前i个部门中分j个组的最优答案,有:
    f(i,j)=min{f(x,j1)+(ix1)×(sum(i)sum(x))}
    其中sum(i)表示点权的前缀和。
    乍一看这个方程是O(n3)的,但根据一些简单的证明,对于同一个ij增大时,最优决策点也是右移的,于是我们就缩小了决策范围,又根据上面那个组会越来越大的结论,可以证明(然而我不会)时间复杂度是O(n2logn)的,可以通过此题(我也不知道怎么过的…但它就是能过)。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll inf=1000000000;
    int n,b,s,m,first[5010]={0},tot=0;
    int u[50010],v[50010];
    ll c[50010],dis[5010],sum[5010];
    ll f[2][5010],t[5010];
    bool vis[5010]={0};
    queue<int> Q;
    struct edge
    {
        int v,next;
        ll w;
    }e[50010];
    
    void insert(int a,int b,ll w)
    {
        e[++tot].v=b;
        e[tot].next=first[a];
        e[tot].w=w;
        first[a]=tot;
    }
    
    void spfa(int S)
    {
        for(int i=1;i<=n;i++)
            dis[i]=inf;
        dis[S]=0;
        Q.push(S);
        vis[S]=1;
        while(!Q.empty())
        {
            int v=Q.front();Q.pop();
            for(int i=first[v];i;i=e[i].next)
                if (dis[v]+e[i].w<dis[e[i].v])
                {
                    dis[e[i].v]=dis[v]+e[i].w;
                    if (!vis[e[i].v])
                    {
                        Q.push(e[i].v);
                        vis[e[i].v]=1;
                    }
                }
            vis[v]=0;
        }
    }
    
    int main()
    {
        scanf("%d%d%d%d",&n,&b,&s,&m);
        for(int i=1;i<=m;i++)
            scanf("%d%d%lld",&u[i],&v[i],&c[i]);
    
        memset(first,0,sizeof(first));
        tot=0;
        for(int i=1;i<=m;i++)
            insert(u[i],v[i],c[i]);
        spfa(b+1);
        for(int i=1;i<=b;i++)
            sum[i]=dis[i];
    
        memset(first,0,sizeof(first));
        tot=0;
        for(int i=1;i<=m;i++)
            insert(v[i],u[i],c[i]);
        spfa(b+1);
        for(int i=1;i<=b;i++)
            sum[i]+=dis[i];
    
        sort(sum+1,sum+b+1);
        sum[0]=0;
        for(int i=1;i<=b;i++)
            sum[i]+=sum[i-1];
    
        int now=0,past=1;
        memset(f[past],0x3f,sizeof(f[past]));
        f[past][0]=0;
        for(int j=1;j<=s;j++)
        {
            memset(f[now],0x3f,sizeof(f[now]));
            for(ll i=1;i<=b;i++)
            {
                for(ll x=t[i];x<i;x++)
                    if (f[now][i]>=f[past][x]+(i-x-1ll)*(sum[i]-sum[x]))
                    {
                        f[now][i]=f[past][x]+(i-x-1ll)*(sum[i]-sum[x]);
                        t[i]=x;
                    }
            }
            swap(now,past);
        }
        printf("%lld
    ",f[past][b]);
    
        return 0;
    }
  • 相关阅读:
    多种的android进度条的特效源码
    仿360手机卫士界面效果android版源码
    非常不错的KPTimePicker效果源码
    jquery超级简单的后台系统自适应框架
    jquery银行电子账单表格填入和编辑插件
    jquery核心基础
    javascript基础的一些总结
    查看HA 的stat
    查看Linux服务器序列号
    TCPDUMP收集某个端口的流量
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793308.html
Copyright © 2020-2023  润新知