DP妥妥的,看数据规模,首先需要离散化,其次转移的时间复杂度不能超过O(n^2),应当是O(n)左右
Point.1 某个规律
因为4和7互质,所以必然存在某个值k,使大于k的任意数都能分解为4和7的组合,手动模拟出这个数是18。理论上讲扩欧应该能够算出来。。。
Point.2 离散化
由于格子太多显然不能作为数组下标,但药堆数不大,所以读入时先储存下药粒数和位置信息,按位置排序。结合point.1,相邻两堆药之间的距离大于18时,实际上和等于18没差,因为后一个状态肯定能由前一个转移过来,所以重构格子序列,把所有大于18的间隔缩成18,这样就把格子编号缩到1800000范围内,就可以作为下标实现转移了。
Point.3 初始化
初始时f[i]一定要设成极小值,保证每次更新ans的f[i]都是已被更新过的,否则结果会比正解大;f[0]设为0,不然绝壁输出负数。
Point.4 转移
枚举每个位置(离散化后),f[i-4],f[i-7],f[i]取最大值。
参考代码:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
long long a[1800005],f[1800005];
int n;
long long m,ans,x,y;
struct mc
{
long long num,pos;
bool operator <(const mc b) const
{
return pos<b.pos;
}
}e[100005],p[100005];
void dp()
{
long long st=0;
for (int i=1;i<=n;i++)
scanf("%d%lld",&e[i].num,&e[i].pos);
sort(e+1,e+n+1);
long long sx=0;
e[0].num=0;
e[0].pos=0;
for (int i=1;i<=n;i++)
{
if (!(e[i].pos-e[i-1].pos))
{
a[st]+=e[i].num;
continue;
}
if (e[i].pos-e[i-1].pos>=18)
{
st+=18;
a[st]=e[i].num;
}
else
{
st+=e[i].pos-e[i-1].pos;
a[st]=e[i].num;
}
}
for (int i=1;i<=st;i++)
{
f[i]=-210000000;
if (i>=4)
f[i]=max(f[i-4]+a[i],f[i]);
if (i>=7)
f[i]=max(f[i-7]+a[i],f[i]);
if (ans<f[i]) ans=f[i];
}
return;
}
int main()
{
freopen("hop.in","r",stdin);
freopen("hop.out","w",stdout);
scanf("%d%lld",&n,&m);
memset(a,0,sizeof(a));
memset(f,0,sizeof(f));
long long sy=0;
dp();
printf("%lld",ans);
return 0;
}