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

来源:互联网 发布:大数据英文名称 编辑:程序博客网 时间:2024/04/28 06:54

直线上有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
原创粉丝点击