直线上有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的,...等价于...