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 凸包优化后缀和?

posted @ 2022-10-19 21:55  Mystletainn  阅读(46)  评论(0)    收藏  举报