首页 > 其他分享 >【LeetCode动态规划#17】知道秘密的人,维护多个dp数组

【LeetCode动态规划#17】知道秘密的人,维护多个dp数组

时间:2023-08-26 21:11:25浏览次数:44  
标签:forget 17 int 秘密 delay 知道 分享 LeetCode dp

知道秘密的人数

在第 1 天,有一个人发现了一个秘密。

给你一个整数 delay ,表示每个人会在发现秘密后的 delay 天之后,每天 给一个新的人 分享 秘密。同时给你一个整数 forget ,表示每个人在发现秘密 forget 天之后会 忘记 这个秘密。一个人 不能 在忘记秘密那一天及之后的日子里分享秘密。

给你一个整数 n ,请你返回在第 n 天结束时,知道秘密的人数。由于答案可能会很大,请你将结果对 109 + 7 取余 后返回。

示例 1:

输入:n = 6, delay = 2, forget = 4
输出:5
解释:
第 1 天:假设第一个人叫 A 。(一个人知道秘密)
第 2 天:A 是唯一一个知道秘密的人。(一个人知道秘密)
第 3 天:A 把秘密分享给 B 。(两个人知道秘密)
第 4 天:A 把秘密分享给一个新的人 C 。(三个人知道秘密)
第 5 天:A 忘记了秘密,B 把秘密分享给一个新的人 D 。(三个人知道秘密)
第 6 天:B 把秘密分享给 E,C 把秘密分享给 F 。(五个人知道秘密)

示例 2:

输入:n = 4, delay = 1, forget = 3
输出:6
解释:
第 1 天:第一个知道秘密的人为 A 。(一个人知道秘密)
第 2 天:A 把秘密分享给 B 。(两个人知道秘密)
第 3 天:A 和 B 把秘密分享给 2 个新的人 C 和 D 。(四个人知道秘密)
第 4 天:A 忘记了秘密,B、C、D 分别分享给 3 个新的人。(六个人知道秘密)

提示:

2 <= n <= 1000
1 <= delay < forget <= n

思路

简单来说就是将当前的人分为三类:知道秘密的人、遗忘秘密的人、正在分享秘密的人

然后使用者三类人进行互推与计算。

知道秘密的人可能会与正在分享秘密的人有重合

例如:A在第一天知道了秘密,假设延迟天数delay为3,那么他在第3天可以分享秘密,如果第3天A把秘密分享给了B,那么现在知道秘密的人就有2个,但是正在分享秘密的人还是只有A一个,因为B还没到延迟天数

代码

在实现上,我们需要维护3个dp数组,具体如下:

class Solution {
public:
    int peopleAwareOfSecret(int n, int delay, int forget) {
        long long mod = 1000000007;
        vector<int> dpKnow(n + 1, 0);//第i天知道秘密的人(还不能分享,因为还没到delay天,可以分享后会转变为dpShare)
        vector<int> dpForget(n + 1, 0);//第i天忘了秘密的人
        vector<int> dpShare(n + 1, 0);//第i天可以分享秘密的人(注意,这部分人与知道秘密的人是有重合的)

        //初始化分为两部分:初始化第一天时三个数组的值、根据第一天的值计算出后面天数中可以确定的值
        //第1天的时候,知道秘密的人1个
        //第1天的时候,将要忘记秘密的人0个
        //第1天的时候,可以分享秘密的人0个
        dpKnow[1] = 1;//后两个就不用填了
        
        //然后我们依据dpKnow在第一天的情况,计算出dpForget和dpShare在后续天数中的值
        //那么这个人在forget天之后会忘掉秘密,因此我们可以求出1 + forget天时dpForget的情况
        if(1 + forget <= n){//假设第一个人是A,那么A会在1 + forget天时忘掉秘密,此时忘掉秘密的人只有他一个
            dpForget[1 + forget] = 1;
        }
        //第一个知道秘密的人肯定是要到1 + delay天之后才可以分享,因此dpShare在1 + deelay天时的值为1
        if(1 + delay <= n){
            dpShare[1 + delay] = 1;
        }

        //第二天之后!
        for(int i = 2; i <= n; ++i){
            //我们也还是先看第i天有几个人知道秘密,根据dpKnow的含义可得计算方法如下:
            //dpKnow[i] = dpKnow[i - 1] - dpForget[i] + dpShare[i]
            //即:第i天知道秘密的人 = 第i - 1天知道秘密的人 - 第i天忘掉秘密的人 + 第i天被分享了秘密的人
            dpKnow[i] = (mod + dpKnow[i - 1] - dpForget[i] + dpShare[i]) % mod;

            //然后计算之后会忘记的人,可以参考初始化时的计算方式:第一天有1个人,那么这个人在1 + forget天会忘掉
            //同理,第i天新增了多少知道秘密的人,那么这些人在i + forget天后就会忘掉秘密
            if(i + forget <= n){//i + forget天前知道秘密的早忘了,i + forget之后知道的肯定不会忘
                dpForget[i + forget] = dpShare[i];
            }

            //最后计算第i天新增了多少可以分享秘密的人
            //来到第i天时,可以分享秘密的人是:第i天熬过了delay天的新增加的知道秘密的人和之前可以分享秘密的人(并且这些人到第i天还没忘)
            //举个例子:
            //第7天,当前有一堆可以分享秘密的人.他们中有一些可能是第6天满足delay期限后变成可以分享的,也有一些可能已经分享了几天并且快忘了的
            //然后第7天还会产生一堆被分享后知道秘密的新人,这些新人现在还不能分享秘密
            //那么等到了7 + delay天,此时在第7天产生的新人就都可以分享秘密了,并且还留有一部分第7天已经在分享秘密并且7 + delay天还没忘的人
            //这两部分人共同构成了7 + delay天可以分享秘密的人的人员构成,也就是dpShare[7 + delay]
            if(i + delay <= n){
                dpShare[i + delay] = (mod + dpShare[i + delay - 1] - dpForget[i + delay] + dpShare[i]) % mod;
                //dpShare[i]是在第i天新增的人,这些人在i + delay天时可以分享秘密
                //dpShare[i + delay - 1] - dpForget[i + delay]是之前能够分享秘密的人,但这些人到了i + delay天要忘掉一批人(即减掉i + delay当天的遗忘人数dpForget[i + delay])
            }  
        }
        return dpKnow[n];
    }
};

标签:forget,17,int,秘密,delay,知道,分享,LeetCode,dp
From: https://www.cnblogs.com/DAYceng/p/17659460.html

相关文章

  • OpenJDK17.0.8字节码解读样例
    因为JDK17将会成为未来5至10年里Java应用的主流JDK,刚好闲着没事,就想着将《深入理解Java虚拟机》一书中关于字节码的解读样例在OpenJDK17.0.8上看看变化有多大!先把实验环境说明一下:OS:Windows10专业版 22H2JDK:openjdkversion"17.0.8"2023-07-18LTS源......
  • 剑指Offer 17. 打印从1到最大的n位数
    题目链接:剑指Offer17.打印从1到最大的n位数题目描述:输入数字n,按顺序打印出从1到最大的n位十进制数。比如输入3,则打印出1、2、3一直到最大的3位数999。解法思路:利用上题中的代码,快速计算出10^n的值,然后依次将结果加到ans代码:funcprintNumbers(nint)[]int{......
  • P7424 [THUPC2017] 天天爱射击
    传送门我们发现,考虑每个子弹击碎哪些木板是不现实的,所以我们要转换问题:考虑每个木板被哪个子弹击碎考虑可持久化线段树,转换问题成求区间\(l\simr\)的第s早发射的子弹,模板题上代码:#include<bits/stdc++.h>#definelllonglongusingnamespacestd;constllN=2e5+50,M=2e5......
  • LeetCode 463.岛屿的周长
    1.题目:给定一个 rowxcol 的二维网格地图 grid ,其中:grid[i][j]=1 表示陆地, grid[i][j]=0 表示水域。网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。岛屿中没有“湖”(......
  • Leetcode 228. 汇总区间
    1.题目描述给定一个无重复元素的有序整数数组nums。返回恰好覆盖数组中所有数字的最小有序区间范围列表。也就是说,nums的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于nums的数字x。列表中的每个区间范围[a,b]应该按如下格式输出"a-......
  • LeetCode 131. 分割回文串
    题目链接:LeetCode131.分割回文串题意:给你一个字符串s,请你将s分割成一些子串,使每个子串都是回文串。返回s所有可能的分割方案。回文串是正着读和反着读都一样的字符串。解题思路:dfs算法的过程其实就是一棵递归树,所有的dfs算法的步骤大概有以下几步:找到中止条件:即......
  • LeetCode-26. 删除有序数组中的重复项(Java)
    这是我在51CTO博客开启的写作之路,第一次正式写博客记录我在LeetCode的刷题日,希望能帮助更多的小伙伴攻面自己心仪的公司offer。如下对于 LeetCode-26. 删除有序数组中的重复项,进行全面解析并小结解题思路,同学们请参考:1.题目描述给你一个 升序排列 的数组 nums ,请你 原地 删......
  • [CF1794E] Labeling the Tree with Distances 题解
    [CF1794E]LabelingtheTreewithDistances题解题目描述给你一个树,边权为\(1\)。给定\(n-1\)个数,你需要将这些数分配到\(n-1\)个节点上。一个点\(x\)是好的,当且仅当存在一种分配方案,所有被分配数的点到\(x\)的最短路径长度等于其被分配的数。求所有好点。思路从......
  • 801. 使序列递增的最小交换次数(状态机dp)
     dp的本质就是图论状态机dp就是包含多个待选状态,个人感觉就是分层图,每一层是一个状态,不同状态之间有可以相互转化的方法。通过状态和状态之间的关系,来实现状态转移。本题f[i][j]表示只从前i项中选,f[i][0]表示第i项不进行交换,f[i][1]表示第i项进行交换,达到严格递增情况下所需......
  • CF1746F
    题目链接。这个数据范围,显然出题人出这题的本意不是让我们用带修莫队过题(当然有人过),而我们又难以找到很好的\(\text{DS}\)维护方法。故考虑另辟蹊径。对于所有\(a_i,x\),不妨把值相同的归入一个等价类。对于一个等价类,随机出一个\([0,1]\)间的权值。我们把等价类的权值代替......