Max Dot
首先很自然地转化:把 \(X_i\) 的单调性转变为构造时每次后缀区间加。
于是题目可以这么表达: 有 \(N\) 种物品,第 \(i\) 种物品体积为 \(V_i=\frac{1}{n-i+1}\),价值为 \(W_i=\frac{\sum_{j=i}^n A_j}{n-i+1}\),我们要取 \(S\) 个物品,在体积不超过 \(M\) 的情况下最大化价值。
但我不会写这个背包。。。
———————— 下面是我的心路历程 ————————
于是开始考虑贪心。常见思路是先让条件都满足,得到一个可能解,然后再进行调整。同时易得若 \(v_i>v_j , w_i<w_j\) 那么 \(j\) 绝对不可能被选上。且若最终体积没到 \(M\) 肯定不优。
所以按照 \(w\) 升序排序。先只取第 \(1\) 个和第 \(n\) 个物品且总体积为 \(M\)(若达不到那么全取 \(n\) 就好了,直接输出)。
然后挨个尝试替换,即换 \(L_1\) 个 \(1\) ,\(L_2\) 个 \(n\) 。又因为 \(w_1<w_i<w_n\) 所以一定可以找到
\[L_1\times w_1+L_2\times w_2 = (L_1+L_2)\times w_i
\]
若
\[L_1\times v_1+L_2\times v_2 < (L_1+L_2)\times v_i
\]
则能替换。
突然发现若能替换,因为个数可以为实数个,那么直接把一方换完最优,所以最后只剩两种物品。
那再推广一下,刚开始无论取哪两种,调整后还是两种。
那不就直接枚举两种物品就好了吗????(但我始终纠结 \(O(n)\) 做法浪费两三小时)
所以就得到了 \(O(n^2)\) 的做法:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 5005
ll a[N],suf[N];
int n;
double m,s,ans;
struct node{
double v,w;
int t;
}d[N];
bool cmp(node x,node y){
return x.w<y.w;
}
bool cmp2(node x,node y){
if(x.t!=y.t)return x.t<y.t;
return x.w<y.w;
}
int main(){
cin>>n>>m>>s;
for(int i=1;i<=n;++i){
cin>>a[i];
}
for(int i=n;i;--i)suf[i]=suf[i+1]+a[i];
for(int i=1;i<=n;++i){
d[i].w=1.0/(n-i+1);
d[i].v=d[i].w*suf[i];
d[i].t=0;
}
sort(d+1,d+1+n,cmp);
double maxn=-1e12;int del=0;
for(int i=1;i<=n;++i){
if(d[i].v<maxn)d[i].t=1,++del;
maxn=max(maxn,d[i].v);
}
sort(d+1,d+1+n,cmp2);
n-=del;
if(d[n].w*s<=m||n==1){
ans=d[n].v*s;
printf("%.20lf\n",ans);
return 0;
}
double len1=(m-s*d[n].w)/(d[1].w-d[n].w),len2=s-len1;
for(int i=1;i<n;++i){
for(int j=i+1;j<=n;++j){
len1=(m-s*d[j].w)/(d[i].w-d[j].w),len2=s-len1;
if(len1>=0&&len2>=0)ans=max(ans,len1*d[i].v+len2*d[j].v);
}
}
//ans=max(ans,len1*d[l].v+len2*d[r].v);
printf("%.20lf\n",ans);
return 0;
}
听说有 \(O(n)\) 做法。插个眼,之后来补:arc128 C 凸包优化后缀和?
浙公网安备 33010602011771号