zoukankan      html  css  js  c++  java
  • 【BZOJ4873】寿司餐厅(六省联考2017)-最小割

    测试地址:寿司餐厅
    做法:本题需要用到最小割。
    首先,仔细看题,计算价钱中的c数而不是数,那么我们发现每种贡献都仅会被计算一次,考虑把这些东西建成点,于是就变成了选点问题。
    我们发现有以下一些限制条件:
    1.如果要选区间[l,r],必须同时选区间[l,r1][l+1,r]
    2.如果要选第i种寿司,那么同时会产生ai的费用。
    3.如果代号为i的寿司被选中过一次以上,会产生mi2的费用。
    那么我们把区间建成点,点权为选它对答案产生的贡献,在计算[i,i]这种区间的贡献时,可以把上面第2条的费用算进去,不然另建出一个点也可以。由于同种代号的寿司的第3条贡献只会被计算一次,因此我们还要对所有代号建出点,点权为mi2,当我们选[i,i]时,必然要选代号为ai的点。我们发现这就变成了一个最大权闭合子图的问题,是经典的最小割应用,建模后跑一遍最大流即可。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll inf=(ll)1000000000*(ll)1000000000;
    int n,m,S,T,a[110],id[110][110],totid=0;
    int first[10010]={0},tot=1,h,t,q[10010],lvl[10010],cur[10010];
    ll sum=0;
    struct edge
    {
        int v,next;
        ll f;
    }e[100010];
    
    void insert(int a,int b,ll f)
    {
        e[++tot].v=b,e[tot].next=first[a],e[tot].f=f,first[a]=tot;
        e[++tot].v=a,e[tot].next=first[b],e[tot].f=0,first[b]=tot;
    }
    
    void init()
    {
        scanf("%d%lld",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
            for(int j=i;j<=n;j++)
                id[i][j]=++totid;
        S=totid+1001+1,T=totid+1001+2;
        for(int i=1;i<=n;i++)
            for(int j=i;j<=n;j++)
            {
                ll val;
                scanf("%lld",&val);
                if (j>i)
                {
                    if (val<0) insert(id[i][j],T,-val);
                    else sum+=val,insert(S,id[i][j],val);
                    insert(id[i][j],id[i][j-1],inf);
                    insert(id[i][j],id[i+1][j],inf);
                }
                else
                {
                    val-=(ll)a[i];
                    if (val<0) insert(id[i][i],T,-val);
                    else sum+=val,insert(S,id[i][i],val);
                    insert(id[i][i],totid+a[i]+1,inf);
                }
            }
        for(int i=0;i<=1000;i++)
            insert(totid+i+1,T,m*(ll)i*(ll)i);
    }
    
    bool makelevel()
    {
        for(int i=1;i<=totid;i++)
            lvl[i]=-1,cur[i]=first[i];
        for(int i=1;i<=n;i++)
        {
            lvl[totid+a[i]+1]=-1;
            cur[totid+a[i]+1]=first[totid+a[i]+1];
        }
        lvl[S]=0;cur[S]=first[S];
        lvl[T]=-1;
        h=t=1;
        q[1]=S;
        while(h<=t)
        {
            int v=q[h++];
            for(int i=first[v];i;i=e[i].next)
                if (e[i].f&&lvl[e[i].v]==-1)
                {
                    lvl[e[i].v]=lvl[v]+1;
                    q[++t]=e[i].v;
                }
        }
        return lvl[T]!=-1;
    }
    
    ll maxflow(int v,ll maxf)
    {
        ll ret=0,f;
        if (v==T) return maxf;
        for(int i=cur[v];i;i=e[i].next)
        {
            if (e[i].f&&lvl[e[i].v]==lvl[v]+1)
            {
                f=maxflow(e[i].v,min(maxf-ret,e[i].f));
                ret+=f;
                e[i].f-=f;
                e[i^1].f+=f;
                if (ret==maxf) break;
            }
            cur[v]=i;
        }
        if (!ret) lvl[v]=-1;
        return ret;
    }
    
    void dinic()
    {
        ll maxf=0;
        while(makelevel()) maxf+=maxflow(S,inf);
        printf("%lld",sum-maxf);
    }
    
    int main()
    {
        init();
        dinic();
    
        return 0;
    }
  • 相关阅读:
    js正则表达式中的问号使用技巧总结
    380. Insert Delete GetRandom O(1)
    34. Find First and Last Position of Element in Sorted Array
    162. Find Peak Element
    220. Contains Duplicate III
    269. Alien Dictionary
    18. 4Sum
    15. 3Sum
    224. Basic Calculator
    227. Basic Calculator II
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793443.html
Copyright © 2011-2022 走看看