题意:有n个城市并排着,每个城市有些珠宝,有两个人站在第s个城市准备收集珠宝,两人可以各自行动,但两人之间的距离不能超过dis,而且每经过一个城市就需要消耗1天,他们仅有t天时间收集珠宝,问最多能收集多少珠宝?
思路:
其实就是类似一个滑动窗口在收集一个序列上的权值。首先两个人可以同时往两边散开,直到极限距离dis(这样省时),然后他们可能往左/右走,也可能往左走一会儿再往右走,也可能往右走一会儿再往左走。可以看出其实这些考虑都是对称的,那么我们主要考虑1边就行了。可以尝试枚举往左边走k天,其他t-k天往右走(利用前缀和)。注意有可能dis是个奇数的,那么在t充足的情况下,应该两边扩展到dis-1就停下呢,还是往左/右一点呢?还得靠枚举,左边试一下,右边试一下。dis也可能比t还大,那么最多也就是同时往两边扩展t天。
情况不算多,但是写起来还是得很小心的。还有,数据必须用longlong了。对称情况只需要反转一下序列,改变一下起点s。时间复杂度O(n)。
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <algorithm>
#include <vector>
#include <iostream>
#define pii pair<int,int>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int N=;
const double PI = acos(-1.0);
LL w[N], sum[N];
int n, dis, s;
LL get_ans(int p1,int p2,int t)
{
LL big=sum[p2]-sum[p1-];
LL ans=big;
for(int k=; k<p1&&t>=k; k++) //枚举给左边k天,其他给右边
{
LL left=sum[p1-]-sum[p1-k-], right=;
int r=t-*k; //若大于0,则可继续往右走
if( r> ) right=sum[min(n,p2+r)]-sum[p2]; //右边还能获得珠宝
ans=max(ans, big+left+right);
}
return ans;
} LL cal(int t) //初始定点p1,p2必须拉到最长
{
int p1=max(, s-min(dis/,t));
int p2=min(n, s+min(dis/,t));
LL ans=;
t-=dis/;
if(dis%==) //奇数
{
if( t< || p1==&&p2==n ) return get_ans(p1,p2, t); //t已无剩
if( p1> ) ans=max(ans, get_ans(p1-,p2, t-)); //左坑
if( p2<n ) ans=max(ans, get_ans(p1,p2+, t-)); //右坑
}
else //偶数,不需要占坑
{
ans=max(ans, get_ans(p1,p2, t));
}
return ans;
} int main()
{
freopen("input.txt", "r", stdin);
int t;LL ans;
while(~scanf("%d%d",&n,&s))
{
memset(sum, , sizeof(sum));
for(int i=; i<=n; i++)
{
scanf("%d",&w[i]);
sum[i]+=sum[i-]+w[i];
}
scanf("%d%d",&dis,&t); //dis是两人的距离上限,t是所有的时间
ans=cal(t); reverse(w+,w+n+); //反过来继续求。
memset(sum, , sizeof(sum));
for(int i=; i<=n; i++) sum[i]=sum[i-]+w[i];
s=n+-s;
ans=max(ans, cal(t));
cout<<ans<<endl;
}
return ;
}
AC代码