每日一题 | 环形排列问题

时间:2022-07-23
本文章向大家介绍每日一题 | 环形排列问题,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

昨日问题

每日一题 | QQ群撩妹问题

这道题是codeforces的D题,链接:https://codeforces.com/contest/1395/problem/D

这道题有一定难度,不容易想到解法。我们先来分析一下问题,n天有n句撩妹的话可以选择,但是中间可能会被禁言,禁言了之后不可以发言,但是同样会消耗话术。有可能选择分数大的话术虽然会禁言,但是可以收益更大,也有可能因为禁言导致了会浪费很多话术不能使用。

我们想要简单地贪心获得解好像不太可能,因为我们并不知道禁言多还是禁言少比较有利,假如说我们知道应该禁言的次数,是不是就可以贪心了呢?

我们很容易发现禁言是必须的,因为杀伤力小于m的话术数量是小于等于n的。我们假设杀伤力大于m的骚话有k句,小于m的有l句,首先我们可以得到k + l = n。我们最少需要禁言(n - l + d) / (d+1)次,由于n - l = k,所以是(k + d) / (d+1)次,最多禁言k次。

我们可以遍历禁言的次数,遍历完了之后,到底要禁言多少天呢?其实这是一个波动值,因为如果禁言发生在结尾,可能并不会禁言满d天。比如我们最后一天说骚话,因为已经是最后一天了,所以禁言d天也就不生效了。

禁言x次的话,最少需要多少天呢?也很容易算,最少需要天,也就是把最后一次禁言放在最后一天。这样意味着我们可以填充更多的普通骚话。

想到这里就简单了,我们只需要遍历一下禁言的次数,用贪心的方法算一下每一种禁言方案获得的最大值就可以了。我们只需要保证我们能说话的时候选得都是杀伤力最大的,禁言的时候选的话术并不重要。我们只需要把所有的话术按照是否大于m分成两个部分进行倒排即可。

提供不能AC代码,因为Python性能太差,会超时。

n, d, m = map(int, input().split(' '))

records = list(map(int, input().split(' ')))

a, b = [], []

for i in records:
    if i > m:
        a.append(i)
    else:
        b.append(i)

a = sorted(a, reverse=True)
b = sorted(b, reverse=True)


ret = 0
for i in range((len(a) + d) // (d+1), len(a)+1):
    # 禁言i次最少需要消耗l天
    l = (i-1) * (d+1) + 1
    # 我们选择杀伤力最大的前i句骚话以及n-l个空缺说普通骚话
    s = sum(a[:i]) + (0 if l >= n else sum(b[:n-l]))
    ret = max(ret, s)

print(ret)

今日问题

环形排列问题

给定一个n,代表1-n这n个整数,我们知道n个不同的数的排列有种。在排列当中,我们做一个奇怪的操作,对于整数i,我们选取排列当中i左边最大的j,使得,在i和j当中连接一条无向边。再选取排列中i右侧的最小j,使得,同样连一条无向边。

比如[3, 1, 4, 2],我们可以得到的边是(3, 4), (1, 3), (1, 4), (2, 4),我们可以发现可以构成一个环 3 -> 1 -> 4。请问给定一个整数n,要求所有排列当中可以通过这种方式构成环的排列有多少种?

n的范围是10的6次方。

- END -