减肥计划

题目

在这里插入图片描述
在这里插入图片描述

分析

  • 首先要注意

同一个旅游景点最多去一次

  • 所以我们就可以考虑 01背包
  • 想一下 ∑ i ∈ S b i ∑ i ∈ S a i \frac{\sum_{i\in S} b_i}{\sum_{i\in S} a_i} iSaiiSbi 不就是 d p [ i ] i \frac{dp[i]}{i} idp[i] ,其中 d p [ i ] dp[i] dp[i]表示使用了 i i i的能量获得的最大价值。
  • 那么是不是感觉这个题特别简单啊
  • 但是注意看复杂度,这样是 O ( n m ) O(nm) O(nm)的,也就是 O ( n ∗ ∑ i = 1 i ≤ n a i ) O(n*\sum_{i=1} ^{i\leq n}a_i) O(ni=1inai) 的,显然这样是过不去的,但是可以拿60分
  • 那么我们这里考虑一下如何优化一下这个算法
  • 现在局限我们的就是这个 m m m,那么怎么让他变小呢?
  • 考虑一下这个式子的性质
  • 这个式子的结果一定是在子集中单个物品的比值最大值与最小值的区间内的
  • 所以对于一个已知的 d p [ i ] dp[i] dp[i] 当我们把其中比值最小的 i i i取出后,答案一定是非降的。
  • 所以我们只用枚举到 110000 110000 110000即可( m + a [ i ] m+a[i] m+a[i]
  • 对啦对啦,我们要放在外面枚举打擂台,因为更新的时候可能不是当前最大的,会造成操作冗余

题外话

有没有兄弟看到 ∑ i ∈ S b i ∑ i ∈ S a i \frac{\sum_{i\in S} b_i}{\sum_{i\in S} a_i} iSaiiSbi这个式子,立马就想分数规划的,请停止你这种罪恶的想法,他是不对的。
你二分枚举 m i d mid mid是一个近似的答案, c h e c k check check 是不是能有满足限制的数量,那么你有没有想过一个问题,你选的数的比值必然是一个连续的区间(在全部里面)。
那么如果出现:
最后一个我需要它来凑够限制,但是倒数后几个代价很小,而且比值也没有答案大(相当于负优化,这个证明一下即可),那么我们就需要把倒数后几个都删掉,留下最后一个凑够限制即可。
那么这个操作应该不好做,而且这不就相当于上面的 D P DP DP
n 个 物 品 选 m 个 代 价 最 小 价 值 最 大 且 大 于 限 制 n个物品选m个代价最小价值最大且大于限制 nm

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, w, m;
int dp[52300010];
int a[N], b[N];
inline int read() {
    int Num = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        Num = (Num << 1) + (Num << 3) + ch - '0';
        ch = getchar();
    }
    return Num * f;
}
inline int gcd(int x, int y) { return !y ? x : gcd(y, x % y); }
int main() {
    n = read(), w = read();
    for (int i = 1; i <= n; i++) a[i] = read(), b[i] = read(), m += a[i];
    if (m < w) {
        puts("-1");
        return 0;
    }
	for(int i=1;i<=m;i++) dp[i]=-1;
    dp[0]=0;
    double maxx = 0;
    int Bit = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 110000-a[i]; j >= 0; j--) 
            if(dp[j]>=0) 
				dp[j+a[i]] = max(dp[j+a[i]], dp[j] + b[i]);
	for(int i=w;i<=110000;i++) if(dp[i]>=0)
	{
		 double minn = (double)dp[i] / i * 1.0;
	            if (minn > maxx && i >= w) {
	                Bit = i;
	                maxx = minn;
	           }
	}
    printf("%d/%d", dp[Bit] / gcd(dp[Bit], Bit), Bit / gcd(dp[Bit], Bit));
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值