【题解】动态规划 |牛客动态规划题单 1002 舔狗舔到最后一无所有

动态规划1002

分析:

最关键的是找到状态转移方程,题目中第 i i i 天的方案数与第 i − 1 i-1 i1 i − 2 i-2 i2 天的选择有关,不妨假设三家店为:0号,1号与2号。如果前面两天吃的是 0 号店,那么第 i i i 天势必不能吃这0号店,方案数就是第 i − 1 i-1 i1天吃1号店与2号店的方案数之和与第 i − 2 i-2 i2 天吃1号店与2号店的方案数之和。列表说明比较直观:
第一行为天数,第一列为选择的种类,交叉出的格子表示某天选择某店时总的方案数
表中的第一行是天数,第一列是当天选择的店,高亮的8就表示第3天选择0号店的总方案数是8种。
那么8是如何计算得到的呢?框出来的部分就是计算8所需要的数据,被框起来的是此前两天选择1号店和2号店的总方案数,题目规定不能三天连吃同一家店,因此,从第三天起,就要考虑前面的选择对后续选择的影响。
第一天时无论选择哪种方案,总方案数都是1(这里说的总方案数是某天选择某一家店的总方案数,题目要求的答案是所有方案,选择三家店的总方案数要加起来);
第二天是可以和第一天吃同一家店的,因此第二天的那一列每一行的计算都是前一列三家店总方案数的和,或者说第二天的选择是不受限的,因此第一天吃哪家第二天都可以吃0号、1号、2号。
但第三天就不一样了,以第三天选择0号为例,同一行代表的是第一天吃0号店、第二天吃0号店,那么第三天显然不能继续吃0号店,因此第一行的前两格对于第三格的计算是没有贡献的,而第一天吃1号或者2号店(第一列的2、3格)就保证了无论第二天吃哪家,第三天都可以吃0号,因此方案数都对第三天吃0号这一格有贡献,具体来说就是需要加和,第二天同理,第二天吃1号或2号就可以保证无论第一天吃哪家,第三天都可以吃0号,也有贡献,也要加进结果,因此最终的8是由这四个部分加和得到的。
因为动态规划的计算过程是可列表的,因此可以现将题目范围天数计算出来,再根据读入的天数查表输出。

单写一下递推公式:dp[day][j]表示在day这天吃j号的可能方案总数。
dp[day][j] = dp[day - 1][(j + 1) % 3] + dp[day - 1][( j + 2 ) % 3] +  dp[day - 2][(j + 1) % 3] + dp[day - 2][(j + 1) % 3]

AC代码如下:

#include<bits/stdc++.h>
using namespace std;

const int mod = 1e9 + 7;//查错半天结果是模数打错了。。写成1e9 + 10了,结果差了超级多。。 

typedef long long ll;//8 bytes 2400000B = 24000K

ll f[100010][3];
void dp()
{
    memset(f, 0, sizeof(f));
    f[1][0] = 1;  f[1][1] = 1;  f[1][2] = 1;
    f[2][0] = 3;  f[2][1] = 3;  f[2][2] = 3;
    int day = 3;
    for( ; day <= 100000; day++)
    {
        for(int j = 0; j < 3; j++)
        {
            f[day][j] = (f[day - 1][(j + 1) % 3] + f[day - 1][(j + 2) % 3] + f[day - 2][(j + 1) %3] +f[day - 2][(j + 2) % 3]) % mod;
        }
    }
}

int main()
    
{
    int T;
    cin>>T;
    dp();
    while(T--)
    {
        int n;
        cin>>n;
        cout<<(f[n][0] % mod + f[n][1] % mod + f[n][2] % mod) % mod<<endl;
    }
    return 0;
}

提交记录:

WA了两次,第一次纯属思路错误,因为写成了三列的形式,所以不需要在计算过程中乘以3,只需要在最后输出时对天数所在行进行求和取模;第二次才发现模数打错了,习惯是开数组的时候直接加10余量,打模数那行的时候没过脑子,模数虽然只差了3,但是累积下来最后误差差了两倍多,百思不得其解,后来从头读题才突然发现,这种东西还是复制为妙。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值