Uva 11054 链接
Uva 11054强调的是劳动力(不是酒),劳动力是不能抵消的,都必须加起来(不管是进,还是出),如果是负,就用绝对值。本人理解,这里劳动力相当于系统的内耗。我们可以把一个村庄或多个村庄一起看成一个村庄,但同时必须加上其系统的内耗。
举例:
a[5] = {2,3,-4,-2,1} //sum of a[i]=0
2,3,-4,-2,1所需的劳动力等价于5,-4,-2,1所需的劳动力+2
5,-4,-2,1所需的劳动力等价于1,-2,1所需的劳动力+5
1,-2,1所需的劳动力等价于-1,1所需的劳动力+1
-1,1所需的劳动力等价于0所需的劳动力+1 (绝对值)
所以总共劳动力为2+5+1+1=9。
代码如下(参考紫书答案, 例题8-5) :
long long WineLabor(long long a[], int len) {
long long last, ans;
for (int i=0; i<len; i++) {
ans += abs(last);
last += a[i];
}
return ans;
}
其ans就是所需累计劳动力,若a[5]={2,3,-4,-2,1},ans的值分别为0,2,7,8,9。last 就是sum of a[i],其值为2,5,1,-1,0。
注意:
1) n个村庄的劳动力计算为n-1次。我们算ans只需要算到a[n-2],即倒数第2个元素。因为倒数第2个村庄和倒数第1个村庄之间的劳动力算1次即可。比如说最后2个村庄的a[i]值为[-3,3],可知需要3个劳动力。
2) 此题还有一个环形变种 UVA - 11300 Spreading the Wealth。先记下来,以后再做。
Uva 11093链接
Uva 11093里面,汽车每个加油站先加油x1,然后消耗掉x2,净剩x1-x2。该题里面每个加油站一进一出,只需计算净剩值(注意跟Uva11054不一样),而不是绝对值之和。
注意:
1) 该题有环,可以用i=(i+1)%n,也可以将一个环扩展成两个环,从而像链表一样操作。
方法1:一环变两环。(参考了https://menyifan.com/2015/10/18/uva11093/)
#include <iostream>
using namespace std;
const int maxn=100000+5;
int n, p[maxn], q[maxn];
int travel(int p[], int q[], int n) {
int finish = 0;
int ans = -1;
for (int start=0; start<n; start=finish) {
finish = start;
int fuel = 0, sum = 0;
while ((ans<0) && (fuel>=0)) {
fuel+=p[finish]-q[finish];
finish++;
sum++;
if ((sum==n) && (fuel>=0))
ans=start;
}
if (ans>=0)
return ans+1; //因为下标从0开始算
}
return ans;
}
int main()
{
int T;
scanf("%d", &T);
for (int k=1; k<=T; k++) {
scanf("%d", &n);
for (int i=0; i<n; i++) {
scanf("%d", &p[i]);
p[i+n]=p[i];
}
for (int i=0; i<n; i++) {
scanf("%d", &q[i]);
q[i+n]=q[i];
}
int ans = travel(p,q,n);
printf("Case %d: ", k);
if(ans < 0)
cout<<"Not possible"<<endl;
else
cout<<"Possible from station "<<ans<<endl;
}
return 0;
}
注意while()的条件。这里必须用fuel>=0,而不能用sum<=n。否则中途fuel<0时while()仍然会继续,从而使得某个不合格的站点混水摸鱼。
例如:
input p[] = {1,3,1,4,2,1,2,3,2}
input q[] = {2,2,1,3,1,3,1,1,1}
当start = 0时,debug如下
start=0 finish=0 fuel=0
start=0 finish=1 fuel=-1 //这里while()仍会继续
start=0 finish=2 fuel=0
start=0 finish=3 fuel=0
start=0 finish=4 fuel=1
start=0 finish=5 fuel=2
start=0 finish=6 fuel=0
start=0 finish=7 fuel=1
start=0 finish=8 fuel=3
find! start=0 finish=9
Case 1: Possible from station 1
方法2:还是用经典的i = (i+1)%n。这也是紫书的解法。注意当输出的ans大于起始站点时说明已经wrap,也即无解。
代码如下:
int travel2(int p[], int q[], int n) {
int start = 0, finish =0, fuel = 0;
while(1) {
//重新开始
finish = start+1;
fuel = p[start] - q[start];
while (finish != start) { //no wrap,注意这里不可以用finish > start, 否则wrap后自动退出
if ((fuel < 0))
break;
fuel += p[finish] - q[finish];
finish = (finish + 1) % n;
}
if (finish > start)
start = finish;
else if (finish < start)
return -1;
else //finish == start, 走完一圈,但是还是要看fuel
if (fuel >= 0)
return finish + 1; //因为下标从0开始算
else
return -1;
}
}
注意以上两种方法复杂度都是O(n)。因为虽然有两个循环嵌套,但每个站点都只算了一次。