题目大意:
有n件物品,第
i
i
i件重量
a
i
a_i
ai,坐标
i
i
i,若你现在身上有重量之和为w的物品,那么位移一个单位距离的代价是w+1。一开始你在0,最后要回到0,问花费代价不超过m的情况下,能够有的最大重量之和是多少。n<=1e5,m<=1e6.
题解:显然代价是两倍的最远距离加上每件物品代价是
i
a
i
ia_i
iai。这是个01背包,直接做显然是不行的,但是注意到代价关于收益的增长实际是非常快的(显然收益不会超过代价)因此设f(i,v)表示后i个物品,获得v的收益至少需要多少代价。注意到某个
k
a
k
≥
i
a
k
(
k
≥
i
)
ka_k\ge ia_k(k\ge i)
kak≥iak(k≥i),因此
v
>
m
/
i
v>m/i
v>m/i的部分是不可能的,因此时间复杂度不超过调和级数。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
namespace INPUT_SPACE{
const int BS=(1<<24)+5;char Buffer[BS],*HD,*TL;inline int gc() { if(HD==TL) TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);return (HD==TL)?EOF:*HD++; }
inline int inn() { int x,ch;while((ch=gc())<'0'||ch>'9');x=ch^'0';while((ch=gc())>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');return x; }
}using INPUT_SPACE::inn;const int N=100010,M=1000010;int a[N],f[M];
int main()
{
int n=inn(),m=inn();rep(i,1,m) f[i]=m+1;rep(i,1,n) a[i]=inn();
for(int i=n;i;i--) if(a[i]<=m/i)
{
for(int j=m/i,v=i*a[i];j>a[i];j--) f[j]=min(f[j],f[j-a[i]]+v);
f[a[i]]=min(f[a[i]],(i<<1)+i*a[i]);
}
for(int i=m;i;i--) if(f[i]<=m) return !printf("%d\n",i);return !printf("0\n");
}