洛谷 P3749 [六省联考2017]寿司餐厅 最小割

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/89446858

题目:
https://www.luogu.org/problemnew/show/P3749

分析:
显然选择了一个区间 [ l , r ] [l,r] ,那么一定要选他的子区间。因为区间的价值有正负,所以考虑最大权闭合子图。
S S 向每个权值为正区间 [ l , r ] [l,r] 连边, [ l , r ] [l,r] 向负区间连边,每个区间 [ l , r ] [l,r] [ l + 1 , r ] [l+1,r] [ l , r 1 ] [l,r-1] 连边。
我们发现断掉负权点的边相当于选择吃这一个区间,那么也会有费用。
每个区间向区间内的每一种食物连inf的边,每种食物向T连 x x 的边,其中 x x 为类型。每种食物向他的类型连inf的边,每种类型向T连 m x 2 m*x^2 的边。
这样一来,我们需要同时断掉三个部分才能代表吃的部分。先要扣掉美味值,还要付类型费用,每一个食物还要付独立费用。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#define LL long long

const int maxn=107;
const int N=2e4+7;
const LL inf=1e13;

using namespace std;

int n,m,x,cnt,s,t,tot;
LL ans;
int id[maxn][maxn],w[maxn];
int vis[N],num[N],ls[N],dis[N];

struct edge{
    int y,op,next;
    LL w;
}g[N*50];

queue <int> q;

void add(int x,int y,LL w)
{
    g[++tot]=(edge){y,tot+1,ls[x],w};
    ls[x]=tot;
    g[++tot]=(edge){x,tot-1,ls[y],0};
    ls[y]=tot;
}

bool bfs()
{
    for (int i=s;i<=t;i++) dis[i]=0x3f3f3f3f;
    while (!q.empty()) q.pop();
    dis[s]=0;
    q.push(s);
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        for (int i=ls[x];i>0;i=g[i].next)
        {
            int y=g[i].y;
            if ((g[i].w) && (dis[y]>dis[x]+1))
            {
            	dis[y]=dis[x]+1;
            	if (y==t) return true;
            	q.push(y);
            }
        }
    }
    return false;
}

LL dfs(int x,LL maxf)
{
    if ((x==t) || (!maxf)) return maxf;
    LL ret=0;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if ((g[i].w) && (dis[y]==dis[x]+1))
        {
            LL f=dfs(y,min(maxf-ret,g[i].w));
            if (!f) dis[y]=-1;
            g[i].w-=f;
            g[g[i].op].w+=f;
            ret+=f;
            if (ret==maxf) break;
        }
    }
    return ret;
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        for (int j=i;j<=n;j++)
        {
            id[i][j]=++cnt;
        }
    }
    int now=cnt;
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
        if (!vis[w[i]])
        {
            vis[w[i]]=++cnt;
            num[cnt-now]=w[i];
        }
    }	
    s=0,t=cnt+n+1;
    for (int i=1;i<=n;i++)
    {
        for (int j=i;j<=n;j++)
        {
            scanf("%d",&x);
            if (x>0) add(s,id[i][j],x),ans+=(LL)x;
                else add(id[i][j],t,-x);
            for (int k=i;k<=j;k++) add(id[i][j],cnt+k,inf);
            if (i<j)
            {
                add(id[i][j],id[i+1][j],inf);
                add(id[i][j],id[i][j-1],inf);
            }
        }
    }
    for (int i=1;i<=n;i++)
    {
        add(cnt+i,t,w[i]);
        add(cnt+i,vis[w[i]],inf);
    }
    for (int i=now+1;i<=cnt;i++) add(i,t,m*num[i-now]*num[i-now]);	
    while (bfs()) ans-=dfs(s,inf);
    printf("%lld\n",ans);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/89446858