102 best牛围栏(枚举、实数二分)

1. 问题描述:

农夫约翰的农场由 N 块田地组成,每块地里都有一定数量的牛,其数量不会少于 1 头,也不会超过 2000 头。约翰希望用围栏将一部分连续的田地围起来,并使得围起来的区域内每块地包含的牛的数量的平均值达到最大。围起区域内至少需要包含 F 块地,其中 F 会在输入中给出。在给定条件下,计算围起区域内每块地包含的牛的数量的平均值可能的最大值是多少。

输入格式

第一行输入整数 N 和 F,数据间用空格隔开。接下来 N 行,每行输入一个整数,第 i+1 行输入的整数代表第 i 片区域内包含的牛的数目。

输出格式

输出一个整数,表示平均值的最大值乘以 1000 再向下取整之后得到的结果。

数据范围

1 ≤ N ≤ 100000
1 ≤ F ≤ N

输入样例:

10 6

4
2
10
3
8
5
9
4
1

输出样例:

6500
来源:https://www.acwing.com/problem/content/104/

2. 思路分析:

分析题目可以知道直接做不好做,当我们已知一个平均值avg的时候,判断是否存在一个方案使得平均值是大于等于avg,这个时候就比较好做了,我们可以尝试使用使用二分查找来解决(二分的时候多一个平均值的条件),这样我们可以枚举所有的avg,判断是否满足条件即可,最小的avg = 0,最大的avg = max(a[i]),i = 1...n,并且可以验证可以使用二分来解决的,当前的mid= (l + r) / 2,如果发现当前的mid大于等于当前的avg,说明答案是在mid的右边,否则答案在mid的左边。那么我们如何在二分的时候判断当前的avg是否满足条件呢?也即判断[1,n]的区间中是否存在连续的一段使得平均值是大于等于avg的,我们可以先使a[i] - avg,然后判断是否在[1,n]中存在连续的一段使得区间和是大于等于0的,类似于dp的枚举方式我们可以将所有的区间根据右端点是a[1],a[2],...a[n]进行分类,其中a[k]表示以当前的a[k]结尾的所有区间,包括a[1:k],a[2:k],a[3:k]...a[k:k],求解区间中一段连续的和可以预处理出前缀和,然后求解出每一类中的最大值那么就可以得到最终的答案:

由题目可以知道是整数的二分所以我们在二分的时候满足r > l,当判断当前满足条件的时候令l = mid或者是r = mid,由于存在误差所以需要最终的答案是r * 1000而不是l * 1000,乘以1000之后l与r的误差就会变大那么取整之后两个答案就有一定差距了。

3. 代码如下:

from typing import List


class Solution:
    def check(self, avg: float, F: int, a: List[float]):
        s = [0.0]
        # 注意a的长度已经是包括0之后的长度了
        n = len(a)
        for i in range(1, n):
            s.append(s[-1] + a[i] - avg)
        mins = 0
        for k in range(F, n):
            mins = min(mins, s[k - F])
            if s[k] >= mins: return True
        return False

    def process(self):
        n, F = map(int, input().split())
        # a前面加上一个0可以使得下标可以从1开始
        a = [0]
        l = r = 0
        for i in range(n):
            a.append(int(input()))
            r = max(r, a[-1])
        # 对于实数的二分如果保留三位数字那么多取两位, 并且保证r > l
        while r - l > 10 ** -5:
            mid = (l + r) / 2
            if self.check(mid, F, a):
                l = mid
            else:
                r = mid
        # 注意在需要使用r而不是l, 因为l与r是有一定差距的并且r > l, 所以扩大1000倍之后那么取整之后就有一定的差距了
        return int(r * 1000)


if __name__ == "__main__":
    print(Solution().process())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用 HanLP 和 scikit-learn 实现的重复二分文本聚类代码,具体实现思路为: 1. 使用 HanLP 进行中文分词,得到文本的词语列表。 2. 使用 TF-IDF 对文本进行特征提取。 3. 使用重复二分 K-Means 算法进行文本聚类。 ```python from pyhanlp import * import numpy as np from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.cluster import KMeans from sklearn.metrics.pairwise import cosine_similarity # 设置分词器 tokenizer = JClass("com.hankcs.hanlp.tokenizer.StandardTokenizer") # 加载停用词 with open('stopwords.txt', 'r', encoding='utf-8') as f: stopwords = set([line.strip() for line in f]) # 对文本进行分词 def segment(text): words = [word.word for word in tokenizer.segment(text)] words = [word for word in words if word not in stopwords] return words # 重复二分 K-Means 算法 def repeated_bisection_kmeans(X, k, n_repeats=10): best_labels = None best_inertia = np.inf for i in range(n_repeats): labels = KMeans(n_clusters=k).fit_predict(X) centroids = np.zeros((k, X.shape[1])) for j in range(k): centroids[j] = np.mean(X[labels == j], axis=0) sim = cosine_similarity(X, centroids) inertia = sum([max(sim[i]) for i in range(X.shape[0])]) if inertia < best_inertia: best_labels = labels best_inertia = inertia return best_labels # 加载文本数据 with open('data.txt', 'r', encoding='utf-8') as f: texts = [line.strip() for line in f] # 对文本进行分词 segments = [segment(text) for text in texts] # 计算 TF-IDF 特征 vectorizer = TfidfVectorizer(tokenizer=lambda x: x, lowercase=False) X = vectorizer.fit_transform(segments).toarray() # 进行文本聚类 k = 3 # 聚成3类 labels = repeated_bisection_kmeans(X, k) # 输出聚类结果 for i in range(k): print('Cluster %d:' % i) for j in range(len(texts)): if labels[j] == i: print(texts[j]) print('==================================') ``` 需要注意的是,代码中使用了一个停用词列表,可以根据实际情况调整停用词列表。此外,代码中的文本数据需要以一行一条的形式保存在 data.txt 文件中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值