P4377 [USACO18OPEN]Talent Show
一开始他还不是题解,是草稿……
题解
设 \(x_i\) 表示第 \(i\) 个点选或不选,可得
\[ans=max(\frac{ \sum_{i=1}^{n}(x_i\times t_i)}{\sum_{i=1}^{n}(x_i\times w_i)})
\]
我们不妨设一个函数如下
\[y=ans\times \sum_{i=1}^{n}(x_i\times w_i)-\sum_{i=1}^{n}(x_i\times 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\n",l,r);
mid=(l+r)/2;
if(check(mid))
l=ans=mid;
else r=mid;
}
printf("%d\n",(int)(ans*1000));
}