如何唤醒房间中最多的人?

时间:2019-03-02 06:00:48

标签: python algorithm optimization max dynamic-programming

我遇到一个问题,那就是您可以唤醒一个正在睡觉的人,这个人的状态从代表睡觉的S变为代表醒来的.

当您唤醒一个人时,由于距离很近,旁边的人也会从睡眠中醒来。例如,有三个人在一个房间里睡觉。如果您在中间唤醒一个人,那么三个人都将醒来,即SSS变成...

但是,如果您唤醒已经清醒的人,那么他的状态将不会改变。但是他旁边的人会醒着,即S.S变成...

问题是给定一个房间s,里面有熟睡的人S和清醒的人.。唤醒k个人之后,该房间中最多可容纳多少人。

一个棘手的例子是s = S.SSSSk = 2

  1. 唤醒人s[4],那么房间将是S.S...
  2. 唤醒人s[1],那么房间将是......

也就是说,您可以在唤醒s[4]人和s[1]人之后使房间中的每个人都醒着。因此,此示例的答案将是6个人,而不是5

我用python列出了所有可以唤醒的人的索引组合来编写我的解决方案。唤醒他们并计算清醒的人数。

它仅适用于短字符串和少量人员。

我认为我需要在算法水平上进行一些改进。任何建议或暗示将不胜感激。 预先感谢。

import sys
import itertools

memo = {}

def wake(s, i):
    if i in memo: 
        return memo[i]
    s = list(s)
    for n in i:
        if n == 0:
            s[n], s[n+1] = '.', '.'
        elif n == len(s)-1:
            s[n-1], s[n] = '.', '.'
        else:
            s[n-1], s[n], s[n+1] = '.', '.', '.'
    memo[i] = s.count('.')
    return memo[i]

if __name__ == '__main__':
    with open(sys.argv[1]) as f:
        line = f.readline()
        n, k = map(int, list(line.rstrip().split(' ')))
        line = f.readline()
        s = line.rstrip()

    ans = 0
    for i in itertools.combinations(range(n), k):
        temp = wake(s, i)
        if temp > ans:
            ans = temp

    print(ans) 

出于测试目的,我将在下面放置一些测试用例和正确答案。

Format:
n k
s
ans

5 3
.....
5

12 2
.SSSS.SS.SSS
9

7 1
..S.SS.
6

67 4
SS.S...S....SS..S..S.S.S...SS...SSS..SS.SS.SSSSS...S.S.S...S......S
48

29 2
.S.....SSSSS.SSSS...SS.SSS.SS
18

79 6
.S...SSS.SSS..SSSS.SSSSS.SS.S.SS.SS.SSSSSSS.SS...SS.S.SSS.SS.S.SS..S..S.SSS.SS.
47

41 9
.S.S...S.S.....S.SS.SS.SS.SS.S.SS.SS.....
41

91 67
...SS.SS....S...S.S.SS...SSS.SSSSSSS.....SS..S.S.SS...S..S..S.SS.......SSSSS.S.S...S.SS.S.S
91


7 97
SSSS.SS
7

276 23
SSSSSS.SSS..SSS.SS.S.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.SS.SSSSSSSSSSSSSSSSSSS.SSSSSS.SSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSS.S.SSS.SSSSSS.SSSSSSSSSSS.SSSSSSSSSSSSS.SSSSSSS.S.SSSS.SSSSSSSSSSSS.SSSSSSSSSSSSSSSS.SSS.S.SS.SSSSSSS.SSSSSSSS.SSS.S.
100

3 个答案:

答案 0 :(得分:0)

如果我们先与dp[n][k]个人打交道并且已经醒来的'S'人,让n是我们醒来的k人的数量。对于dp[n][k],我认为没有理由唤醒人数n,因为没有人n+1我们可以唤醒(因为我们首先只处理n人),因此更好地唤醒人n-1,也使人n醒来。我稍后会使用这种逻辑。

然后,如果n <= 3,我们可以唤醒所有n个人,只要我们有动静,即k > 0。如果我们没有动作,那么答案就是已经醒着的人数。

如果n > 3

  • 如果为k = 0,则为dp[n][k] = dp[n][0] = 0,因为我们没有唤醒任何人。
  • 如果k > 0,则dp[n][k] = max(dp[n - 3][k - 1] + s[n - 3 : n].count('S'), dp[n - 1][k])。在这里,我们处理2种情况:如果我们唤醒了n-1人,那么第n-2, n-1, n个人醒了,输了1步,所以我们算出刚刚醒来的人数(s[n - 3 : n].count('S') ),并检查其余n-3人和k-1个动作的最大数量:dp[n - 3][k - 1]。如果我们没有唤醒任何人,那么如果第n人处于初始状态,那么我们还有k个动作。因此,在这种情况下,答案为dp[n - 1][k]

答案是max(dp[n][k]) + s.count('.')dp[n][k]仅处理了熟睡的人数,但我们叫醒了他们。因此,要获得完整答案,我们只需添加已经醒着的人数。

ans = 0

if n <= 3:
    if k > 0:
        ans = n
    else:
        ans = s.count('.')
else:
    dp = [[0 for x in range(k + 1)] for y in range(n)]
    dp[2][1] = s[:3].count('S')
    for i in range(3, n):
        t = s[i - 3 : i].count('S')
        for j in range(1, k + 1):
            dp[i][j] = max(dp[i - 3][j - 1] + t, dp[i - 1][j])

    for i in range(2, n):
        ans = max(ans, dp[i][k])

    ans += s.count('.')

print(ans) 

答案 1 :(得分:0)

这是我的解决方法。

import pandas as pd


def count_all(text, string):
    pos = string.find(text)
    if len(string) == 0 or pos == -1:
        return 0
    return 1 + count_all(text, string[pos+len(text):])


def calculate_ans(input_str, k)->int:
    if 'check':
        if len(input_str) == 0 or \
                (input_str.find('S') == -1 and input_str.find('.') == -1):
            return 0

        if k <= 0 or pd.isna(k):
            return input_str.count('.')

    for cur_str in ['SSS', 'S.S', 'ST.TS', 'SS', 'S']:
        count_ns = min(count_all(cur_str, input_str), k)
        if count_ns > 0:
            if cur_str != 'S.S':
                input_str = input_str.replace(cur_str, len(cur_str) * '.', int(count_ns))
            else:
                input_str = input_str.replace(cur_str, 'T.T', int(count_ns))
            k -= count_ns
        if k == 0:
            break

    return input_str.count('.') + input_str.count('T')


def carson_demo():
    df = pd.read_csv('table.csv')
    for index, (input_str, k, answer) in df.iterrows():
        ans = 0 if pd.isna(input_str) else calculate_ans(input_str, k)
        df.at[index, 'answer'] = int(ans)  # update
    df['answer'] = df['answer'].astype(int, errors='ignore')
    df['k'] = df['k'].astype(int, errors='ignore')
    df.to_csv('table.csv', index=False)


if __name__ == '__main__':
    carson_demo()

table.csv(之前)

input_str,k,answer
.....,3,
.SSSS.SS.SSS,2,
..S.SS.,1,
SS.S...S....SS..S..S.S.S...SS...SSS..SS.SS.SSSSS...S.S.S...S......S,4,
.S.....SSSSS.SSSS...SS.SSS.SS,2,
.S...SSS.SSS..SSSS.SSSSS.SS.S.SS.SS.SSSSSSS.SS...SS.S.SSS.SS.S.SS..S..S.SSS.SS.,6,
.S.S...S.S.....S.SS.SS.SS.SS.S.SS.SS.....,9,
...SS.SS....S...S.S.SS...SSS.SSSSSSS.....SS..S.S.SS...S..S..S.SS.......SSSSS.S.S...S.SS.S.S,67,
SSSS.SS,97,
SSSSSS.SSS..SSS.SS.S.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.SS.SSSSSSSSSSSSSSSSSSS.SSSSSS.SSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSS.S.SSS.SSSSSS.SSSSSSSSSSS.SSSSSSSSSSSSS.SSSSSSS.S.SSSS.SSSSSSSSSSSS.SSSSSSSSSSSSSSSS.SSS.S.SS.SSSSSSS.SSSSSSSS.SSS.S.,23,

table.csv(之后)

input_str,k,answer
.....,3,5
.SSSS.SS.SSS,2,9
..S.SS.,1,6
SS.S...S....SS..S..S.S.S...SS...SSS..SS.SS.SSSSS...S.S.S...S......S,4,48
.S.....SSSSS.SSSS...SS.SSS.SS,2,18
.S...SSS.SSS..SSSS.SSSSS.SS.S.SS.SS.SSSSSSS.SS...SS.S.SSS.SS.S.SS..S..S.SSS.SS.,6,47
.S.S...S.S.....S.SS.SS.SS.SS.S.SS.SS.....,9,41
...SS.SS....S...S.S.SS...SSS.SSSSSSS.....SS..S.S.SS...S..S..S.SS.......SSSSS.S.S...S.SS.S.S,67,91
SSSS.SS,97,7
SSSSSS.SSS..SSS.SS.S.SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.SS.SSSSSSSSSSSSSSSSSSS.SSSSSS.SSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSSSSS.SSSSSSSSSSSSSS.S.SSS.SSSSSS.SSSSSSSSSSS.SSSSSSSSSSSSS.SSSSSSS.S.SSSS.SSSSSSSSSSSS.SSSSSSSSSSSSSSSS.SSS.S.SS.SSSSSSS.SSSSSSSS.SSS.S.,23,100

答案 2 :(得分:0)

解决了Daniel算法中的一个小重叠问题。

ans = 0

if n <= 3:
    if k > 0:
        ans = n
    else:
        ans = s.count('.')
else:
    dp = [[0 for x in range(k+1)] for y in range(n)]
    dp[0][1] = s[:2].count('S')
    dp[1][1] = s[:3].count('S')
    dp[2][1] = max(s[:3].count('S'), s[1:4].count('S'))
    for j in range(2,k+1):
        dp[0][j] = dp[0][1]
        dp[1][j] = dp[1][1]
        dp[2][j] = s[:4].count('S')

    for i in range(3,n-1):
        for j in range(1,k+1):
            dp[i][j] = max(dp[i-3][j-1] + s[i-1:i+2].count('S'),
                           dp[i-1][j])

    for j in range(k+1):
        dp[n-1][j] = dp[n-2][j]

    ans = dp[n-1][k]

    ans += s.count('.')

    print(ans)