「SHOI2017」寿司餐厅 最小割

本文介绍了一道名为“寿司餐厅”的算法竞赛题目,通过最小割算法来解决选择寿司区间的问题,以达到最大收益。文章详细阐述了如何构建网络流图,并针对题目的特殊需求进行了调整。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

「SHOI2017」寿司餐厅

大意

选择某种区间[i,j]可以获得收益(有正有负),选某一种寿司需要付出一定的代价,求收益-代价的最大值

思路

用最小割解决;
将每一个区间作为一个点
将与S在一个集合的作为不选的点,将与T在一个集合的最为选择的点。
与S的连边表示选择的代价,与T的连边作为不选放弃的收益
只需要解决三个问题
1. 边权有正有负
2. 要求对于某种标号有一个m*x*x的代价
3. 符合题意中的区间包含关系
问题1: 与如果选择d[i,j]有正收益的话可以向T连边(不选要放弃这些收益),如果为负的话可以看做是一种代价改为向S连正权变。
问题2: 可以从S向标号pos连(m*x*x)的边,再由pos向归属于pos的寿司连inf边,S向寿司连标号代价的边, 这样选择任何一个寿司就一定要删掉S与pos的边,而选择寿司要花费标号的代价。
问题3: 对于 [i,j] [ i , j ] 这个区间可以从 [i+1,j] [ i + 1 , j ] [i,j1] [ i , j − 1 ] 向它连边就一定可以将所有要加的区间全部限制住
即选择 [i,j] [ i , j ] 就一定要选择这些区间 这么连边和从 [i,j] [ i , j ] 向每一个区间连边是等效的。

代码

精简洗练 又臭又长
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=5000+5;
const int N=5000000+5;
const int inf=0x7ffffff;
struct node
{
    int v,next,w;
}tb[N*2];
int len=1,h[N];
void add1(int i,int j,int w)
{
    len++; tb[len]=(node){j,h[i],w}; h[i]=len;
    len++; tb[len]=(node){i,h[j],0}; h[j]=len;
}
int n,m;
int pos[maxn],d[maxn][maxn];
int hash[N][2],cur[N];
int geth(int x,int y)
{
    return x*n+y;
}
int tot,s,t,ans;
int dis[N/10],q[N],c[N];
bool bfs();
int dfs(int now,int flow);
void dinic();
void solve();
int main()
{
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
    scanf("%d %d",&n,&m);
    s=20000+1; t=s+1;
    for(int i=1; i<=n; i++)
        scanf("%d",&pos[i]);
    for(int i=1; i<=n; i++)
    {
        for(int j=i; j<=n; j++)
        {
            scanf("%d",&d[i][j]);
            int tmp=geth(i,j);
            if(d[i][j]<0)
            {
                if(i!=j)
                {
                    hash[tmp][0]=++tot;
                    add1(s,hash[tmp][0],-d[i][j]);
                }
                else hash[tmp][0]=++tot;
            }
            else
            {
                ans+=d[i][j];
                hash[tmp][0]=++tot;
                if(i!=j)
                {
                    add1(hash[tmp][0],t,d[i][j]);
                }
            }
        }
    }   
    solve();
    return 0;
}
void solve()
{
    for(int i=1; i<=n; i++)
    {
        int tmp=geth(i,i);
        if(!c[pos[i]])
        {
            c[pos[i]]=++tot;
            add1(s,c[pos[i]],m*pos[i]*pos[i]);
        }
        add1(c[pos[i]],hash[tmp][0],inf);
        if(d[i][i]>0)
        {
            add1(s,hash[tmp][0],pos[i]);
            add1(hash[tmp][0],t,d[i][i]);
        }
        else
        {
            add1(s,hash[tmp][0],pos[i]-d[i][i]);
        }
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=i+1; j<=n; j++)
        {
            if(i!=j)
            {
                int tmp1=geth(i,j-1),tmp2=geth(i,j),tmp3=geth(i+1,j);
                add1(hash[tmp1][0],hash[tmp2][0],inf);add1(hash[tmp3][0],hash[tmp2][0],inf);
            }
        }
    }
    dinic();
    cout<<ans<<endl;
}
bool bfs()
{
    memset(dis,-1,sizeof(dis));
    int head=0,tail=1;
    q[1]=s; dis[s]=0;
    while(head<tail)
    {
        head++; int now=q[head];
        if(now==t) return true;
        for(int i=h[now]; i ;i=tb[i].next)
        {
            int v=tb[i].v;
            if(dis[v]==-1 && tb[i].w>0)
            {
                dis[v]=dis[now]+1; tail++; q[tail]=v;
            }
        }
    }
    return false;
}
int dfs(int now,int flow)
{
    if(now==t || flow==0) return flow;
    int tmp=flow;
    for(int &i=cur[now]; i ; i=tb[i].next)
    {
        int v=tb[i].v;
        if(dis[v]==dis[now]+1 && tb[i].w)
        {
            int delta=dfs(v,min(tmp,tb[i].w));
            tmp-=delta; tb[i].w-=delta; tb[i^1].w+=delta;
            if(tmp==0) break;
        }
    }
    return flow-tmp;
}
void dinic()
{
    while(bfs())
    {
        for(int i=0; i<=t; i++)
        {
            cur[i]=h[i];
        }
        ans-=dfs(s,inf);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值