bzoj2879: [Noi2012]美食节

bzoj2879: [Noi2012]美食节


题意

有n(<=40)道菜,第i道菜被点了pi(∑p<=800)次;有m(<=100)个人,第i个人做第j道菜用时tij(<=1000),每人同时只能做一道菜,求所有菜需等待时间和的最小值。(这个题意好差啊 还是去看题面吧…题面也不长的说)


背景

今天noip模拟的T3…算错复杂度,以为很简单,想多路增广,调了2h整,没删windows.h,直接爆零了


题解

脑洞建图神奇优化经典费用流
前置技能:bzoj1070
我们把每个人拆成p个点,第i道菜的点到第j个人的第k个点满流,代表第j个人要做的倒数第k道菜是i。边的费用是它对这个人之后做的菜的影响,也就是k*cost[i][j]。
然后我们优化一下。显然对于一个人,选择靠前的点要比靠后的更优。初始只连所有菜到每个人的第一个点,对于第i个人的第j个点,如果它满流,再把到所有菜到j+1的边连出来。
于是这个费用流就可以跑得很快了。


代码

写得有点丑…

#include<iostream>
#include<cstdio>
#include<cstring>
#define M 6601000
#define N 80050
#define inf 0x3f3f3f3f
using namespace std;
int n,m,S,T,P,p,ans,ct[41][101],ed[101];
int to[M],hd[M],val[M],cst[M],lk[N],cnt=1;
int pre[N],edg[N],q[N],h,t,dis[N];
inline void add(int u,int v,int w,int c)
{
    to[++cnt]=v,hd[cnt]=lk[u],lk[u]=cnt,val[cnt]=w,cst[cnt]=c;
    to[++cnt]=u,hd[cnt]=lk[v],lk[v]=cnt,cst[cnt]=-c;
}
bool inq[N];
bool spfa()
{
    memset(dis,0x3f,sizeof dis);
    h=q[0]=dis[0]=0,t=1;
    while(h!=t)
    {
        inq[p=q[h++]]=0;
        if(h==N)h=0;
        for(int k,i=lk[p];i;i=hd[i])
        if(val[i]&&dis[k=to[i]]>dis[p]+cst[i])
        {
            dis[k]=dis[p]+cst[i];
            pre[k]=p,edg[k]=i;
            if(!inq[k])
            {
                inq[q[t++]=k]=1;
                if(t==N)t=0;
            }
        }
    }
    return dis[T]<inf;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%d",&p),P+=p,add(S,i,p,0);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    scanf("%d",ct[i]+j),
    add(i,n+(j-1)*P+1,1,ct[i][j]);
    T=m*P +n+1;
    for(int i=1;i<=m;i++)
    {
        ed[i]=(i-1)*P+n+1;
        for(int j=1;j<=P;j++)
        add(n+(i-1)*P+j,T,1,0);
    }
    while(spfa())
    {
        ans+=dis[T];
        for(int i=T;i;i=pre[i])
        {
            val[edg[i]]--,val[edg[i]^1]++;
            p=i-n;
            if(i>n&&i<=n+m*P&&pre[i]&&pre[i]<=n&&(i-n)%P&&i==ed[(i-n)/P+1])
            {
                for(int j=1;j<=n;j++)
                add(j,i+1,1,((i-n)%P+1)*ct[j][(i-n)/P+1]);
                ed[(i-n)/P+1]=i+1;
            }
        }
    }
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值