https://vijos.org/p/1091
题解:
一个环,环上的边为行驶这段路所需的油,每个点权为这个点能够给汽车补充的油的数量。问从哪些点能够顺利的跑完一圈。油正好保证跑完全程。
首先是环的问题,通常解决环的问题都使用空间倍增的方法!即复制一份同样的数组,把两个数组连接起来。
预处理a[i]=j,表示从1号点出发到达i点油箱内透支的油的量(其实就是(-1)*油箱内油的量)。a[n+1]到a[2*n]与前面的值相同。
引入升序单调队列,来维护a数组,假如以k点为结束点(也是起始点)的话那么它前面的n-1个a[ ]中的最小值如果大于等于a[k],说明以k点结束可以跑完全程。
可以理解为初始从k出发的时候我就借了a[k]这么多的油放到了油箱中,在跑的过程中所需透支油的最大值我都能满足,说明我能够跑完全程。在过程中,我慢慢的还啊,还啊,最后还清了,也跑到终点了。
#include<stdio.h>
int n,m;
int add[500002];
int dis[500002];
int a[1000002];
int que[1000002];
int main()
{
scanf("%d%d",&n,&m);
int i,j,k;
int sum=0;
for(i=1;i<=n;i++)
{
scanf("%d%d",&dis[i],&add[i]);
sum+=dis[i];
}
dis[1]=m-sum;
sum=0;
for(i=2;i<=n;i++)
{
sum-=add[i-1]-dis[i];
a[i]=sum;
}
a[n+1]=0;
for(i=n+2;i<=n*2;i++)
{
a[i]=a[i-n];
}
int le=1,ri=0;
sum=0;
for(i=2;i<=n*2;i++)
{
if(le<=ri&&i-que[le]>n) le++;
if(i>n)
{
if(a[i]>=a[que[le]])
{
if(sum==0)
{
printf("%d",i-n);
}
else
{
printf(" %d",i-n);
}
sum++;
}
}
while(le<=ri&&a[i]>a[que[ri]]) ri--;
que[++ri]=i;
}
if(sum==0)
{
printf("-1");
}
return 0;
}
……………………………………………………………………………………………………………
……………………………………………………………………………………………………………
但是,后来我发现
这 就是 一 水题
不用这么麻烦,根本不用单调队列
直接预处理gas[i]表示从1号点出发到达i点时油箱内油的量(可以为负),
求出最小值,将所有gas[k]==min的k全部输出就可以了
很好理解,若从k点出发,则相当于使 gas[k]=0
也就是让所有gas[j] (1<=j<=n) 都加上 (-1)*gas[k]
因为gas[k]为最小值,所以 gas[i] 加上 (-1)*gas[k]后一定 >=0
也就是 不会透支
呵呵
#include<stdio.h>
int n,m;
int add[500002];
int dis[500002];
int a[500002];
int main()
{
scanf("%d%d",&n,&m);
int i;
int sum=0;
for(i=1;i<=n;i++){scanf("%d%d",&dis[i],&add[i]);sum+=dis[i];}
dis[1]=m-sum;
for(i=2;i<=n;i++) a[i]=a[i-1]+add[i-1]-dis[i];
int num=0,min=0x7f7f7f7f;
for(i=1;i<=n;i++) if(min>a[i]) min=a[i];
for(i=1;i<=n;i++)
{
if(a[i]==min)
{
if(num==0)printf("%d",i);
else printf(" %d",i);
num++;
}
}
if(num==0) printf("-1");
return 0;
}