bzoj2879 [Noi2012]美食节

这个题一眼看出是修车,但数据范围有点卡,我傻傻地以为写个zkw就万事大吉了,结果发现zkw还没有spfa跑的快......

这个题的关键是动态加边,就是每次找到已经增广到最新的点的厨师给他加一个新点,这样一共需要增广P次,就可以跑出答案了。

View Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define maxn 100000
#define maxm 100000
#define inf 2147483647
using namespace std;
struct et
{
    int s,t,val,cost,next;
}e[maxm];
int fir[maxn],dis[maxn],pre[maxn],q[maxm],t[200][200],now[maxn],cnt[maxn];
bool inque[maxn];
int n,m,tot,st,ed,sum,fare,k,ans;

bool find()
{
    int head=0,tail=1;
    for (int i=st;i<=ed;i++) dis[i]=inf;
    q[1]=st; dis[st]=0; inque[st]=1;
    while (head<tail)
    {
        int now=q[++head];
        for (int j=fir[now];j;j=e[j].next)
        {
            int k=e[j].t;
            if (e[j].val&&dis[now]+e[j].cost<dis[k])
            {
                dis[k]=dis[now]+e[j].cost;
                pre[k]=j;
                if (!inque[k]) q[++tail]=k,inque[k]=1;
            }
        }
        inque[now]=0;
    }
}

void add(int x,int y,int z,int w)
{
    e[++tot].s=x; e[tot].t=y; e[tot].val=z; e[tot].cost=w; e[tot].next=fir[x]; fir[x]=tot;
    e[++tot].s=y; e[tot].t=x; e[tot].val=0; e[tot].cost=-w; e[tot].next=fir[y]; fir[y]=tot;
}

void adjust()
{
    for (int j=1;j<=m;j++)
        if (e[now[j]].val==0) {
            cnt[j]++;
            now[j]=tot+1;
            add(n+sum*(j-1)+cnt[j],ed,1,0);
            for (int i=1;i<=n;i++) add(i,n+sum*(j-1)+cnt[j],1,t[i][j]*cnt[j]);
            return ;
        }
}

void fare_flow()
{
    find();
    fare+=dis[ed];
    for (int j=pre[ed];j;j=pre[e[j].s]) e[j].val--,e[j^1].val++;
    adjust();
}

int main()
{
    freopen("delicacy.in","r",stdin);
    freopen("delicacy.out","w",stdout);
    scanf("%d%d",&n,&m);
    tot=1;
    int x;
    st=0; 
    for (int i=1;i<=n;i++) {
        scanf("%d",&x);
        add(st,i,x,0);
        sum+=x;
    }
    ed=m*sum+n+1; 
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            scanf("%d",&t[i][j]);
            add(i,n+(j-1)*sum+1,1,t[i][j]);
        }
    for (int i=1;i<=m;i++) 
    {
        cnt[i]=1;
        now[i]=tot+1;
        add(n+(i-1)*sum+1,ed,1,0);
    }
    for (int i=sum;i>0;i--) fare_flow();
    printf("%d\n",fare);
    return 0;
}

 

常数大......

转载于:https://www.cnblogs.com/zig-zag/archive/2013/04/19/3031590.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值