BZOJ_4873_[Shoi2017]寿司餐厅_最大权闭合子图

BZOJ_4873_[Shoi2017]寿司餐厅_最大权闭合子图

题意:http://www.lydsy.com/JudgeOnline/problem.php?id=4873

分析:我们发现分数正负都有,并且之间有依赖关系,很容易想到最大权闭合子图。

建图:

1.S向正点连边,负点向T连边。

2.选了[i~j]显然要选[i+1~j]和[i~j-1],分别连边。

3.对于i==j的点,向对应的寿司连边。

4.总花费m*x*x+c*x拆成两部分。对于每个代号x,向T连容量为m*x*x的边,c*x这部分我们考虑算f[i][i]时把f[i][i]的值减掉x,当然也可以每个寿司向T连容量为x的边。

完了。

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
#define inf 100000000
#define LL long long
#define S (30000)
#define T (30001)
int d[110][110],n,m;
int head[31000],to[4000000],nxt[4000000],cnt=1;
int dep[31000],a[110],tot,idx[110][110],mxn;
LL flow[4000000],sum;
inline void add(int u,int v,LL f)
{
    to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;flow[cnt]=f;
    to[++cnt]=u;nxt[cnt]=head[v];head[v]=cnt;flow[cnt]=0;   
}
bool bfs()
{
    queue <int> q;
    memset(dep,0,sizeof(dep));
    dep[S]=1;q.push(S);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=head[x];i;i=nxt[i])
        {
            if(!dep[to[i]]&&flow[i])
            {
                dep[to[i]]=dep[x]+1;
                if(to[i]==T)return 1;
                q.push(to[i]);  
            }
        }
    }
    return 0;
}
LL dfs(int x,LL mf)
{
    if(x==T)return mf;
    LL nf=0;
    for(int i=head[x];i;i=nxt[i])
    {
        if(dep[to[i]]==dep[x]+1&&flow[i])
        {
            int tmp=dfs(to[i],min(flow[i],mf-nf));
            nf+=tmp;
            flow[i]-=tmp;
            flow[i^1]+=tmp;
            if(nf==mf)break;
        }
    }
    dep[x]=0;
    return nf;
}
void dinic()
{
    LL f;
    while(bfs())
    {
        while(f=dfs(S,inf))
            sum-=f; 
    }
    printf("%lld",sum);
}
int main()
{
    register int i,j;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=i;j<=n;j++)
            idx[i][j]=++tot;
    for(i=1;i<=n;i++)scanf("%d",&a[i]),mxn=max(mxn,a[i]);
    for(i=1;i<=mxn;i++)add(tot+i,T,m*i*i);
    for(i=1;i<=n;i++)add(idx[i][i],tot+a[i],inf);
    for(i=1;i<=n;i++)
    {
        for(j=i;j<=n;j++)
        {
            scanf("%d",&d[i][j]);
            if(i==j)d[i][j]-=a[i];
            else{
                add(idx[i][j],idx[i+1][j],inf);
                add(idx[i][j],idx[i][j-1],inf); 
            }
            if(d[i][j]>0)
            {
                sum+=d[i][j];   
                add(S,idx[i][j],d[i][j]);
            }
            else{
                add(idx[i][j],T,-d[i][j]);
            }
        }
    }
    dinic();
}

 

 

转载于:https://www.cnblogs.com/suika/p/8456939.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值