BZOJ 1391: [Ceoi2008]order

Description

有一些任务,需要用到一些机器,可以买可以租,问最大获利.

Solution

网络流.

最大权闭合子图模型.

建图很简单就是S->机器,机器->任务,任务->T.

如果没有租用的话,中间的是INF,不会割掉,加上租用就把容量变成租用的价格即可.

这样求出来的割就是最小损失了,用总数减去即可.

Code

/**************************************************************
    Problem: 1391
    User: BeiYu
    Language: C++
    Result: Accepted
    Time:3864 ms
    Memory:55972 kb
****************************************************************/
 
#include <bits/stdc++.h>
using namespace std;
 
#define debug(a) cout<<#a<<"="<<a<<" "
const int INF = 0x3fffffff;
const int N = 3650;
const int M = 3000500;
 
inline int in(int x=0,char ch=getchar()) { while(ch>'9' || ch<'0') ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();return x; }
 
int n,m;
int val[N],ned[N],c[N];
 
struct NetWork {
    vector< int > g[N];
    struct Edge { int fr,to,fl; }edge[M];
    int s,t,cnte,flow,tot;
    int cur[N],d[N],p[N];
     
    void AddEdge(int fr,int to,int fl) {
        g[fr].push_back(cnte),edge[cnte++]=(Edge){ fr,to,fl };
        g[to].push_back(cnte),edge[cnte++]=(Edge){ to,fr,0 };
    }
    void Build() {
        n=in(),m=in();
        s=0,t=2*m+n+6;
        for(int i=1,x;i<=n;i++) {
            for(x=in(),tot+=x,AddEdge(i,t,x),x=in();x--;) {
                int idx=in(),cost=in();
                AddEdge(n+idx,i,cost);
            }
        }
        for(int i=1;i<=m;i++) {
            c[i]=in();
            int x=n+i;
            AddEdge(s,x,c[i]);
        }
//      for(int i=1;i<=m;i++) cout<<c1[i]<<" "<<c2[i]<<endl;
    }
    int BFS() {
        queue< int > q;
        memset(d,0xff,sizeof(d));
        q.push(s),d[s]=1;
        for(int x;!q.empty();) {
            x=q.front(),q.pop();
            for(int i=0,lm=g[x].size(),v;i<lm;i++) if(edge[g[x][i]].fl && d[(v=edge[g[x][i]].to)]==-1) {
                d[v]=d[x]+1,q.push(v);
            }
        }return d[t]!=-1;
    }
    void Dinic() {
//      debug(s),debug(t)<<endl;
        for(int u,k;BFS();) {
//          cout<<"qwq"<<endl;debug(flow)<<endl;
            for(memset(cur,0,sizeof(cur)),u=s,k=0;;) {
                if(u==t) {
                    int mine=0,minf=INF;
                    for(int i=0;i<k;i++) if(edge[p[i]].fl<minf) minf=edge[p[i]].fl,mine=i;
                    for(int i=0;i<k;i++) edge[p[i]].fl-=minf,edge[p[i]^1].fl+=minf;
//                  for(int i=0;i<k;i++) cout<<edge[p[i]].fr<<"-->";cout<<endl;
//                  debug(minf)<<endl;
                    k=mine,flow+=minf,u=edge[p[mine]].fr;
                }
                for(int &i=cur[u],lm=g[u].size(),v;i<lm;i++) 
                    if(edge[g[u][i]].fl && d[(v=edge[g[u][i]].to)]==d[u]+1) break;
                if(cur[u]<(int)g[u].size()) {
                    p[k++]=g[u][cur[u]],u=edge[g[u][cur[u]]].to;
                } else {
                    if(!k) break;
                    d[u]=-1,u=edge[p[--k]].fr;
                }
            }
        }
    }
    int Maxflow() {
        flow=0,tot=0;
        Build();
        Dinic();
        return tot-flow;
    }
}py;
 
int main() {
    cout<<py.Maxflow()<<endl;
    return 0;
}

  

转载于:https://www.cnblogs.com/beiyuoi/p/6390070.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值