P4377 [USACO18OPEN]Talent Show

一开始他还不是题解,是草稿……

题解

(x_i) 表示第 (i) 个点选或不选,可得

[ans=max(frac{ sum_{i=1}^{n}(x_i imes t_i)}{sum_{i=1}^{n}(x_i imes w_i)}) ]

我们不妨设一个函数如下

[y=ans imes sum_{i=1}^{n}(x_i imes w_i)-sum_{i=1}^{n}(x_i imes t_i) ]

我们以 (ans) 为自变量,(y) 为因变量,剩下的表达式的所有取值为常量,我们就可以画出 (2^n) 方个函数图像,我们可以简单画一下函数图像(随便画的几根线)。

首先,可以得到的是,这个 (ans) 要成立的话是要有一条函数的在取自变量为当前 (ans) 的情况下取 (0) 。(十分显然,不然就不满足我们上面的第一个式子了)

同时我们需要 (ans) 最大,所以我们需要找到最右边的一个满足条件的 (ans) ,在本图中就是黄色直线与 (y) 轴的交点。

然后我们考虑如何找出这个交点,可以发现,对于一个固定的 (ans) ,我们可以通过求所有函数的最小值与 (0) 的关系来求解。如果最小值小于 (0) ,那么 (ans) 偏小;如果大于 (0) ,那么 (ans) 偏大;如果等于,那么就是答案。发现这个东西是满足单调性的,可以二分求解;同时求最小值的操作是可以 (dp) 的,复杂度是满足的。

但是这道题目还给我们添加了一个限制条件,即重量和必须大于等于 (W) ,我们可以考虑在 (dp) 的时候添加限制条件。我们设(f_{i,j}) 表示到第 (i) 个点,且重量和为 (j) 时的最小值是什么,我们可以发现 (w_i) 的值很大,但是 (W) 的值很小,所以我们只需要维护 (1000) 就可以了,大于 (1000) 的都放在一千。

以上。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=255,W=1005;
int n,w;
struct Cow{int w,t;}s[N];
double f[W];
double l,r,mid,ans;
bool check(double now)
{
	f[0]=0;
	for(int i=1;i<=w;++i) f[i]=1e9+7;
	for(int i=1;i<=n;++i)
	{
		for(int j=w;j>=0;--j)
		f[min(j+s[i].w,w)]=min(f[min(j+s[i].w,w)],f[j]+now*s[i].w-s[i].t);
	}
	return f[w]<=0;
}
int main()
{
	cin>>n>>w;
	for(int i=1;i<=n;++i) scanf("%d%d",&s[i].w,&s[i].t);
	l=0,r=250000;
	while(r-l>1e-4)
	{
		// printf("%lf %lf
",l,r);
		mid=(l+r)/2;
		if(check(mid))
		l=ans=mid;
		else r=mid;
	}
	printf("%d
",(int)(ans*1000));
}
原文地址:https://www.cnblogs.com/Point-King/p/13652170.html