动态规划_实战2

网易2017年合唱团

做题经验:
对于一个复杂的题,若是附加条件太多,不能一步到位的解决,可以逐渐的减少附加条件,也就是逐渐的减少参数,等有思路了,再把参数添加进去。

对于这题,作者就是这么想的,一开始,把附加条件d去掉,就是一个很典型的DP问题,然后建模, 递归实现,DP算法实现,最后再把d添加进去。

一、递归实现

这里需要提醒的是,因为能力值有正有负,在下一个值不知道正负性的情况下,是不能确定应该返回哪一个值的,很可能这一次的最小值乘以负数就变成最大值了。
因此不是每次返回一个值,而是每次返回两个值。一个最大值,一个最小值。

import sys

n =  36
ability = [7,-15,31,49,-44,35,44,-47,-23,15,-11,10,-21,10,-13,0,-20,-36,22,-13,-39,-39,-31,-13,-27,-43,-6,40,5,-47,35,-8,24,-31,-24,-1]
k,d = 2, 31

def chorus(n, ability, k):
    if k == 1:
        return [max(ability),min(ability)]
    if k == n:
        result = 1
        for i in range(n):
            result = result * ability[i]
        return [result,result]

    #选中最后一个同学,会得到最大值与最小值
    last_list = chorus(n-1,ability[0:n-1],k-1)
    #未选中最后一个同学,得到最大值与最小值
    no_last_list = chorus(n-1,ability[0:n-1],k)
    #不能认为,index为0的就是max,因为ability[-1]的不确定性,所以可能会不断改变
    #因此需要再每一步都判断一下,得到最大或最小值
    return [max(last_list[0]*ability[-1],
                last_list[1]*ability[-1],
                max(no_last_list)),
            min(last_list[0] * ability[-1],
                last_list[1] * ability[-1],
                min(no_last_list))]

print(chorus(n, ability, k))

二、DP实现(不加d)

这里因为只有两个参数,n和k,分析状态转移方程,可以知道,k一定时,f[k][n_id]的每个状态都可以由k-1的每个状态决定,因此这里只需要维护两个一维数组即可。

代码实现:


n =  36
ability = [7,-15,31,49,-44,35,44,-47,-23,15,-11,10,-21,10,-13,0,-20,-36,22,-13,-39,-39,-31,-13,-27,-43,-6,40,5,-47,35,-8,24,-31,-24,-1]
k,d = 3, 31


def chorus(n, ability, k):
    pre_res_max = []
    pre_res_min = []
    if k == 1:
        return max(ability)
    else:
        for i in range(n):
            pre_res_max.append(max(ability[0:i+1]))
            pre_res_min.append(min(ability[0:i+1]))


    for k_id in range(2,k+1):
        after_res_max = []
        after_res_min = []
        for n_id in range(n):
            if k_id > n_id:
                res = 1
                for i in range(n_id+1):
                    res = res * ability[i]
                after_res_max.append(res)
                after_res_min.append(res)
            else:
                #对于这一点尤其要注意,用哪个元素,千万不能错了。
                #这里要是用n_id,就是49*49了,元素就重复了。
                max1 = pre_res_max[n_id-1]*ability[n_id]
                min1 = pre_res_min[n_id-1] * ability[n_id]
                max2 = after_res_max[n_id-1]
                min2 = after_res_min[n_id-1]
                result_list = [max1,max2,min1,min2]
                after_res_max.append(max(result_list))
                after_res_min.append(min(result_list))

        pre_res_max = after_res_max
        pre_res_min = after_res_min


    return max(pre_res_max)

print(chorus(n, ability, k))

三、DP实现(加d)

因为这里有三个参数n,k,d,需要好好定义一下需要保存的数组。

f[k][i]代表的是,选择k个学生并且以第i个学生作为结尾时的最大(或最小)乘积。

如何定义需要保存的数组,也是一门玄学?

基本上,是根据动态转移方程中的参数的值,来确定需要保存的东西。
比如国王与金矿问题中,下一行的某个状态,由上一行的某两个状态决定,因为需要维护一个一维数组,用来保存上一行的最优值。
然而在LeetCode第62题中,因为状态转移方程是dp[i][j] = dp[i - 1][j] + dp[i][j - 1],当前状态由上一行的第j列与当前行的前一个决定,所以只需要保存两个数据即可。

要根据状态转移方程确定需要保存的东西。

因此在k等于1时,初始化数组时,需要注意初始化的正确性。

建模分析

根据f[k][i]的定义,我们可以得到最优子结构与状态转移方程。
一定要举例说明

  • 1、例如f[3][6],已经选择第7个同学作为结尾,那么ability的第7个值就作为乘积,若是前一个同学选择第6个,那么就是f[2][5] * ability[6],这是其中一个最优子结构,同理,若是倒数第二个同学是第5个,那么就是f[2][4] * ability[6]。

  • 2、理论上,对于n_id,有n_id-1个子结构。因此应该在range(0,n_id)中遍历每个序号作为倒数第二个同学,在遍历的就结果中选择最大最小值。

  • 3、又因为这里加了限制,倒数第二个同学的编号与最后一个编号不能超过d。
    于是,修改循环的取值,若n_id大于d,则从0开始,小于d就从 n_id - d开始
    改为range(max(0, n_id - d), n_id)。
    这里真的是根据题意得到的一个小技巧。

代码实现:

n = int(input())
ability = [int(x) for x in input().split()]
k, d = [int(x) for x in input().split()]


def chorus_dp2(n,ability,k,d):
    # 初始化两个数组,分别存储最大值最小值
    # 其中,f[k][i]代表的是,选择k个学生并且以第i个学生作为结尾时的最大(或最小)乘积
    f_max = []
    f_min = []
    for k_id in range(k):
        f_max.append([])
        f_min.append([])
        for n_id in range(n):
            if k_id == 0:
                #在k等于1 的时候,根据f[k][i]的定义可知,第i个值就应该是ability[i]
                f_max[k_id].append(ability[n_id])
                f_min[k_id].append(ability[n_id])
            else:
                f_max[k_id].append(0)
                f_min[k_id].append(0)

    for k_id in range(1,k):
            res_mid_max = []
            res_mid_min = []
            for j in range(max(0, n_id - d), n_id):
                res_mid_max.append(max(f_max[k_id-1][j] * ability[n_id],
                                       f_min[k_id-1][j] * ability[n_id]))
                res_mid_min.append(min(f_max[k_id-1][j] * ability[n_id],
                                       f_min[k_id-1][j] * ability[n_id]))
            f_max[k_id][n_id] = max(res_mid_max)
            f_min[k_id][n_id] = min(res_mid_min)

    return f_max

print(max(chorus_dp2(n,ability,k,d)[k-1]))


#这段代码并没有考虑k等于n的情况。有bug。
#题目中也没有给k大于n时应该怎么办,是不是允许重复的。题目这一点也出的不好。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容

  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,343评论 0 2
  • 这个不错分享给大家,从扣上看到的,就转过来了 《电脑专业英语》 file [fail] n. 文件;v. 保存文...
    麦子先生R阅读 6,566评论 5 24
  • 人之为人,除肉身之外,还有情感藏在肉身之内。肉身大抵是一样的貌式,情感大抵也是一样的情感。貌式都是一副长身、一只脑...
    曾彧阅读 142评论 0 1
  • 家庭纷争运进入本周。 1,做事没有成果、缺乏上进心、业绩下降,很多计划受阻。 2,疲倦,疲劳,必须多喝水与休息,尽...
    樊易林阅读 420评论 0 0