bzoj1061 志愿者招募

题意:给m类志愿者,第i个志愿者从第 l[ i ] 天工作到第  r[ i ] 天,费用为c[ i ]。每种志愿者可以选择无限多人。

           每一天都有给定的最少志愿者数目要求a[ i ],求费用最小的选择方案,输出费用。

思路:明显是一道线性规划可以解决的问题,但我不会写单纯型。听溪哥说线性规划都有网络流的等价形式,不过我在网上没有找到,知道的dalao可以给我留个言吗。

进入正题:

          假设第i种志愿者选xi人,则有方程 

          A1 * X1 >= a[ 1 ]

          A2 * X2 >= a[ 2 ]

          A3 * X3 >= a[ 3 ]

          ...

         An * Xn >= a[ n ]

         除此之外在开头添加  方程0 = 0。在结尾添加 0 = 0.

         将a[ i ] 移动到 方程左端后变为网络流流量平衡的形式。n + 1个方程对应n+1个节点。

         我们注意到每个变量在方程中连续出现,则差分之后每个变量在两个方程中出现,并且一个系数为正,一个系数为负。

         这就对应网络流中的一条边,这是这个算法的精髓所在,如果xi多次出现的话对应网络流中的多条边,则这个算法直接凉凉。

         而常数项就与原点或汇点连边。

         这里留个坑,一般形式的线性规划如何转化为网络流求解。以后要是做到这类题来补上。

        

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL maxn = 10000;
const LL maxm = 1000000;
const LL inf = 0x3f3f3f3f3f3f3f3f;
struct mcmf{

    LL he[maxn],tot,edge[maxm],ne[maxm],ver[maxm],cost[maxm];

    LL pre[maxn],d[maxn];

    bool vis[maxn];

    LL n;

    void init(LL x){

        n = x;

        tot = 1;

        for( LL i = 0;i <= n;i++ ) he[i] = 0;

    }

    void add(LL x,LL y,LL cap,LL c){

        ver[++tot] = y;

        ne[tot] = he[x];

        he[x] = tot;

        edge[tot] = cap;

        cost[tot] = c;

        ver[++tot] = x;

        ne[tot] = he[y];

        he[y] = tot;

        edge[tot] = 0;

        cost[tot] = -c;

    }

    bool spfa(LL s,LL t){

        queue<LL>q;

        for(LL i = 0;i <= n;i++){

            d[i] = inf;

            vis[i] = false;

            pre[i] = -1;

        }

        d[s] = 0;

        vis[s] = true;

        q.push(s);

        while(!q.empty()){

            LL x = q.front();

            q.pop();

            vis[x] = false;

            for(LL cure = he[x]; cure ;cure = ne[cure]){

                LL y = ver[cure];

                if(edge[cure] && d[y] > d[x] + cost[cure] ){

                    d[y] = d[x] + cost[cure];

                    pre[y] = cure;

                    if(!vis[y]){

                        vis[y] = true;

                        q.push(y);

                    }

                }

            }

        }

        if(pre[t] == -1)return false;

        else return true;

    }



    LL mincost(LL s,LL t,LL &cc){

        LL flow = 0;

        cc = 0;

        while(spfa(s,t)){

            LL mn = inf;

            for(LL cure = pre[t];cure != -1;cure = pre[ ver[cure^1] ]){

                if(mn > edge[cure])

                    mn = edge[cure];

            }

            for(LL cure = pre[t];cure != -1;cure = pre[ ver[cure^1] ]){

                edge[cure] -= mn;

                edge[cure^1] += mn;

                cc += cost[cure] * mn;

            }

            flow += mn;

        }

        return flow;

    }

} g;
LL a[maxn];
int main()
{
    LL n,m,l,r,c,SS,TT;
    scanf("%lld%lld",&n,&m);
    SS = 0;TT = n+2;
    g.init( TT );
    a[0] = 0;
    for( LL i = 1; i <= n;i++ ){
        scanf("%lld",&a[i]);
        LL cur = a[i] - a[i-1];
        if( cur > 0 ){
            g.add( i,TT,cur,0 );
        }else if( cur < 0 ){
            g.add( SS,i,-cur,0 );
        }
    }
    if( a[n] ) g.add( SS,n+1,a[n],0 );
    for( LL i = 1;i <= m;i++ ){
        scanf("%lld%lld%lld",&l,&r,&c);
        g.add( r+1,l,inf,c );
    }
    for( LL i = 1;i <= n;i++ ){
        g.add( i,i+1,inf,0 );
    }
    LL cost;
    LL maxflow = g.mincost(SS,TT,cost);
    printf("%lld",cost);
    return 0;
}

        

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值