uva 11054 Gergovia的酒交易 等价转化/贪心

直线上有n个等距的村庄,每个村庄要么买酒,要么卖酒。把k个单位的酒从一个村庄运到相邻村庄需要k个单位的劳动力。所有村庄供需平衡,问最少需要多少劳动力才能满足所有村庄的需求?

(2<=n<=100000)  (-1000<=ai<=1000)  输出保证在64位带符号整数范围内。




我的解法(贪心):

维护两个队列,提供队列和需求队列,

从左往右扫描村庄,如果a[i]>0,则加入需求队列,如果<0则加入提供队列,

没加入一个a[i]之后,两个队列开始进行交易,各自取队头。



关键证明性证明在后面:


/**==========================================
 *   This is a solution for ACM/ICPC problem
 *
 *   @source:uva 11054  Wine trading in Gergovia
 *   @type:  等价转化/贪心  
 *   @author: wust_ysk
 *   @blog:  http://blog.csdn.net/yskyskyer123
 *   @email: 2530094312@qq.com
 *===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#define REP(i,n)  for(int i=0 ;i<(n) ;i++)
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn=100000    ;
int n;
int a[maxn+4];

int main()
{
    while(~scanf("%d",&n)&&n)
    {
        queue<int>qbuy;
        queue<int>qsell;
        ll cost=0;
        REP(i,n)
        {
            scanf("%d",&a[i]);
            if(a[i]<0 )  qsell.push(i);
            else if(a[i]>0)  qbuy.push(i);
            a[i]=abs(a[i]);
            while(!qsell.empty()&&!qbuy.empty())
            {
                 int buy=qbuy.front();
                 int sell=qsell.front();
                 int sub=min(a[buy],a[sell]);
                 a[buy]-=sub;
                 a[sell]-=sub;
                 cost+=  (ll)abs(buy-sell)*sub;
                 if(!a[buy])  qbuy.pop();
                 if(!a[sell]) qsell.pop();
            }

        }
        printf("%lld\n",cost);
    }

   return 0;
}


加入前k个村庄的总需求为x(x为负数代表提供),那么后面的村庄必须提供x。

这个时候如果村庄k  再向后面的村庄提供酒,达到供需平衡时运费一定不是最便宜的。


假如扫描到了某个村庄k,现在又可提供一些酒,而前面还有若干个村庄需要提供,那么先提供k及以前距离最远的村庄 一定不会比最优解差, 换言之,就是最优解的一种。



这句可以忽略:其实上述那种情况,就算提供给K及以前不是最远距离的也不会使结果恶化,但是如果K及以前有村庄需要提供,而K提供给了后面的村庄,会使结果恶化。



以上是贪心解:


下面是等价转化,也是书上的解法:


如果村庄1需要a1,那么一定是通过村庄2提供的,先算上从2到1运a1的路费,然后问题等价于[1,2]需要从后面的村庄运a1+a2,

如果是负数,那么是可以向后面的村庄提供a1+a2,算上这笔费用,而这都是要经过村庄3的,...等价于...

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值