zoukankan      html  css  js  c++  java
  • bzoj4873 [Shoi2017]寿司餐厅

    Input

    第一行包含两个正整数n,m,分别表示这家餐厅提供的寿司总数和计算寿司价格中使用的常数。
    第二行包含n个正整数,其中第k个数ak表示第k份寿司的代号。
    接下来n行,第i行包含n-i+1个整数,其中第j个数di,i+j-1表示吃掉寿司能
    获得的相应的美味度,具体含义见问题描述。
    N<=100,Ai<=1000

    Output

    输出共一行包含一个正整数,表示Kiana能获得的总美味度减去花费的总钱数的最大值。

    Sample Input

    3 1
    2 3 2
    5 -10 15
    -10 15
    15

    Sample Output

    12
    分析:看出来这是一道网络流题,却不知道该如何建模......
       看到mx^2 + cx这个式子,我想起了bzoj1449的费用递增模型. 然而m和x都是固定的,每次的增量都是x,费用并不是递增的......
       然后我又想:选取若干个不同的寿司,它们之间会产生贡献. 似乎可以用最小割来做,类似bzoj3144的离散变量模型吗? 这道题选择的寿司之间没有任何限制,当然不是离散变量模型了. 
       单纯考虑最小割可以吗? 每次要求选一段连续的区间不好处理啊.
       上述尝试均失败后,果断打了暴力.
       如果把区间看作点,这道题差不多就做完了. 选择了区间[l,r],则必然会选择在[l,r]中的所有的点,这实际上就是最大权闭合子图模型. 每个区间要向所有区间内的点连边吗? 不用!利用传递关系:[l,r]向[l + 1,r]和[l,r - 1]连边.  当l == r时,就代表了一个点.
       费用要怎么处理呢? 将mx^2 + cx看作mx^2 和 cx两部分. 把编号也看作点. 如果一个编号被选择了,那么它的费用就是mx^2,不会改变.  如果一个点被选择了,那么它的费用就是它的编号.  所以每个编号向T连边,容量为m * i^2. 每个点向其所属的编号连边, 容量为inf.  每个点向T连边,容量为其编号.  这样的话选择了点i,就要割掉它到T的所有连边,即i的编号与T的连边和i与T的连边.
       具体的建图方式如下:

    (参考sliverNebula的博客)

        可以这么理解:割掉S与点i的边,就是放弃了i.  割掉i与T的连边,就是选i的代价.

       做网络流的题要有把所有东西都看作“点”的想法.

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 20010,maxm = 120010,inf = 0x7fffffff;
    int n,m,a[110],mx,d[110][110],cnt,pos[110][110],S,T,ans,head[maxn];
    int to[maxm],nextt[maxm],w[maxm],tot = 2,vis[maxn],cur[maxn];
    
    void add(int x,int y,int z)
    {
        w[tot] = z;
        to[tot] = y;
        nextt[tot] = head[x];
        head[x] = tot++;
    
        w[tot] = 0;
        to[tot] = x;
        nextt[tot] = head[y];
        head[y] = tot++;
    }
    
    bool bfs()
    {
        queue <int> q;
        q.push(S);
        memset(vis,-1,sizeof(vis));
        vis[S] = 0;
        while (!q.empty())
        {
            int u = q.front();
            q.pop();
            if (u == T)
                return true;
            for (int i = head[u];i;i = nextt[i])
            {
                int v = to[i];
                if (w[i] && vis[v] == -1)
                {
                    vis[v] = vis[u] + 1;
                    q.push(v);
                }
            }
        }
        return false;
    }
    
    int dfs(int u,int f)
    {
        if (u == T)
            return f;
        int res = 0;
        for (int i = cur[u];i;i = nextt[i])
        {
            int v = to[i];
            if (w[i] && vis[v] == vis[u] + 1)
            {
                int temp = dfs(v,min(f - res,w[i]));
                w[i] -= temp;
                w[i ^ 1] += temp;
                res += temp;
                if (w[i])
                    cur[u] = i;
                if (res == f)
                    return res;
            }
        }
        if (!res)
            vis[u] = -1;
        return res;
    }
    
    void dinic()
    {
        while (bfs())
        {
            for (int i = 1; i <= T; i++)
                cur[i] = head[i];
            ans -= dfs(S,inf);
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d",&a[i]);
            mx = max(mx,a[i]);
        }
        cnt = mx + n;
        for (int i = 1; i <= n; i++)
            for (int j = i; j <= n; j++)
            {
                if (i == j)
                    pos[i][j] = i;
                else
                    pos[i][j] = ++cnt;
            }
        S = cnt + 1;
        T = S + 1;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n - i + 1; j++)
            {
                scanf("%d",&d[i][i + j - 1]);
                int l = i,r = i + j - 1;
                if (d[l][r] < 0)
                    add(pos[l][r],T,-d[l][r]);
                else
                {
                    add(S,pos[l][r],d[l][r]);
                    ans += d[l][r];
                }
                if (l != r)
                    add(pos[l][r],pos[l + 1][r],inf),add(pos[l][r],pos[l][r - 1],inf);
                else
                {
                    add(pos[l][r],a[l] + n,inf);
                    add(pos[l][r],T,a[l]);
                }
            }
        for (int i = 1; i <= mx; i++)
            add(i + n,T,i * i * m);
        dinic();
        printf("%d
    ",ans);
    
        return 0;
    }
  • 相关阅读:
    Python垃圾回收机制 总结
    求二叉树中任意两个结点的距离
    Python 实现排序算法
    InnoDB 索引原理
    Linux常用脚本命令总结
    How Django works?
    RabbitMQ 知识总结
    最短路径之Dijkstra算法和Floyd-Warshall算法
    最小生成树之Prim算法和Kruskal算法
    Python collections模块总结
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8650624.html
Copyright © 2011-2022 走看看