bzoj4873: [Shoi2017]寿司餐厅

bzoj4873: [Shoi2017]寿司餐厅


题意

有n(<=100)种寿司,可以选择任意段连续的寿司,每次得到的美味度是该段所有子区间美味度之和。如果选取了第i种寿司,需要付出代价Ai。对于一个区间,无论被取多少次,其美味度只计算一次。花费同理。当m(一个常数)=1时,如果有代号为x的寿司被选取,需额外花费m*x²。


背景

插一段本来应该在jloi2017滚粗记里的东西
省选题,当时一直调不过样例,最后十分钟发现自己看错题了orz
于是赶快写暴力,出来发现得了5分…是六道题里面得分最低的…严重拉分导致挂了day2
暑假的时候在过网络流专题,想着什么时候把这道题过掉…于是抽了个空写了下中途又和当时一样看错题了 题面里说的是c种不是c个啊 单个寿司的花费不重复计算啊


题解

最大权闭合子图…知道算法就很好yy了
我们认为与S连通的被选,与T连通的不被选。
对于每个区间[l,r]建点,dij>0时,S向[l,r]连dij,否则[l,r]向T连-dij。
选取一个区间[l,r] (l< r)时,一定要选的是[l+1,r]和[l,r-1]。于是[l,r]向这两个区间对应的点连inf。对于[l,l]对应的点,向T连Al,表示花费。
在m=1时,对于每种存在的Ai建点,向T连Ai²;对于代号为Ai的寿司, 向Ai对应的点连inf。


代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 101
#define V 5200
#define M 32000
#define inf 0x3f3f3f3f
using namespace std;
int n,a[N],b[1001],c[N][N],d[N][N],tot=1;
bool m;
int to[M],hd[M],val[M],lk[V],cnt=1,ans;
void add(int u,int v,int w)
{
    to[++cnt]=v,hd[cnt]=lk[u],val[cnt]=w,lk[u]=cnt;
    to[++cnt]=u,hd[cnt]=lk[v],lk[v]=cnt;
}
int q[V],h,t,dis[V],cur[V];
bool bfs()
{
    for(int i=0;i<=tot;i++)
    dis[i]=0,cur[i]=lk[i];
    h=q[0]=0,t=dis[0]=1;
    int x;
    while(h<t)
    {
        x=q[h++];
        for(int k,i=lk[x];i;i=hd[i])
        if(val[i]&&!dis[k=to[i]])
        dis[q[t++]=k]=dis[x]+1;
    }
    return dis[1];
}
int dfs(int x,int v)
{
    if(x==1)return v;
    int cst,ret=0;
    for(int k,&i=cur[x];i;i=hd[i])
    if(val[i]&&dis[k=to[i]]==dis[x]+1)
    {
        cst=dfs(k,v<val[i]?v:val[i]);
        v-=cst,ret+=cst,val[i]-=cst,val[i^1]+=cst;
        if(!v)break;
    }
    if(!ret)dis[x]=-1;
    return ret;
}
int main()
{
    scanf("%d%d",&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++)
    scanf("%d",d[i]+j);
    for(int i=0;i<n;i++)
    for(int j=1;j<=n-i;j++)
    {
        c[j][j+i]=++tot;
        if(d[j][j+i]>0)
        add(0,tot,d[j][j+i]),ans+=d[j][j+i];
        else add(tot,1,-d[j][j+i]);
        if(i)add(tot,c[j+1][j+i],inf),add(tot,c[j][j+i-1],inf);
        else
        {
            if(m)
            {
                if(!b[a[j]])b[a[j]]=++tot,add(tot,1,a[j]*a[j]);
                add(c[j][j],b[a[j]],inf);
            }
            add(c[j][j],1,a[j]);
        }
    }
    while(bfs())ans-=dfs(0,inf);
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值