传送门:小咪买东西
传送门:wyh的物品
这两道题的题目大致相似,解法也大抵相似(双倍经验!!),接下来主要讲解wyh的物品
题目描述:
wyh学长现在手里有 n 个物品,这 n 个物品的重量和价值都告诉你,然后现在让你从中选取 k 个,问你在
所有可能选取的方案中,最大的单位价值为多少(单位价值为选取的 k 个物品的总价值和总重量的比值
首先看到这道题后,我当时是并不知道01规划这个思想的,所以我的第一想法肯定和大部分人一样直接想到了贪心思想(就是比值排一个序然后取前K个),即使当我们得知01规划是正解后我当时还未能看出贪心的错误,后来我想到了一个反例
假设我们有以下这三个分数
100/2 1000000/99999 1/2
如果贪心来说我们选取得肯定是前两个,但是显然因为假分数的问题(如果确保是真分数,我们的贪心就是正确的)
我们此时的分数2并没有分数3来的更加优秀,所以我们的贪心思想就变得假了
既然我们发现我们的贪心思想假了之后,接下来就是我们的正解了01规划
我们观察我们的问题,发现最终的答案显然是
∑
i
=
1
k
v
i
\sum_{i = 1}^k{v_i}
∑i=1kvi
\
\backslash
\
∑
i
=
1
k
c
i
\sum_{i = 1}^k{c_i}
∑i=1kci这个的最大值(选取K个)
我们假设这个式子的值为L
我们得到
∑ i = 1 k v i \sum_{i = 1}^k{v_i} ∑i=1kvi \ \backslash \ ∑ i = 1 k c i \sum_{i = 1}^k{c_i} ∑i=1kci ≥ \geq ≥L
得到 ∑ i = 1 k v i \sum_{i = 1}^k{v_i} ∑i=1kvi — ∑ i = 1 k c i \sum_{i = 1}^k{c_i} ∑i=1kci × \times ×L ≥ \geq ≥ 0
并且当我们得到这样的一个式子之后,和之前的比值就完全不同了,因为是加减法所以此时我们的这个式子是可以拆的,也就是k个```Vi-Ci*L``个式子相加,此时我们在根据这个来排序即可
对于怎么找到我们的L,我们可以选择采用二分来找到,对于每一次我们的L,假设我们的求和式子最终的答案是大于0的话,说明我们的L是可以继续增加因为显然这个式子是关于L单调减的
这就是最朴素的01规划思想了
加下来是我们的代码部分
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
int T;
struct Thing{
double c,v,bizhi;
}thing[maxn];
int n,k;
bool cmp(Thing a,Thing b) {
return a.bizhi>b.bizhi;
}
int check(double mid) {
for(int i=1;i<=n;i++){
thing[i].bizhi=thing[i].v-thing[i].c*mid;
}
sort(thing+1,thing+n+1,cmp);
double ans=0;
for(int i=1;i<=k;i++) {
ans+=thing[i].bizhi;
}
if(ans>=0) return true;
else return false;
}
int main() {
T=read();
while(T--) {
memset(thing,0,sizeof(thing));
n=read();k=read();
for(int i=1;i<=n;i++) {
thing[i].c=read();
thing[i].v=read();
}
double l=0,r=1e7;double ans=0;
while(r-l>=eps) {
double mid=(l+r)/2;
if(check(mid)) {
ans=mid;
l=mid;
}else {
r=mid;
}
}
printf("%.2lf\n",ans);
}
return 0;
}