【算法小讲堂】动态规划!之牛客练习赛41-B题

本文深入解析了一道名为666RPG的DP类型题目,分享了作者在解题过程中遇到的难点及解决思路。文章重点介绍了如何运用滚动数组优化空间复杂度,以及如何确定状态转移方程,为读者提供了详细的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:666RPG

说来实在惭愧,已经很久没有更新博客了。(反正也没人看-.-//偷笑)
那么今天就更新一道dp类型的题目。
所谓:dp大法好,好到头发少。
另外需要补充的一点是呢,这题我在时限内并没有写出来,可谓菜的开花(直接开花)。但是我觉得这个dp的手法十分有用,所以我就很厚脸皮更上了一期,当然也会尽量把思路说的的比较详细啦。
看题!!
牛客练习赛41-B题

思路:

很明显的是一道dp题,但是状态转移方程怎么推,dp数组又怎么开,完全没有思路。
一刚开始以为是倒着dp,而且肯定时按次数dp的,于是先是认为确定距离最后一个数的偶数个数变化成为-666:
比如10个数据的话,那就是10,8,6,4,2转变成-666,因为后面一定是偶数个时才能不变换符号。这样就找到了子问题,但是,问题就来了,dp数组怎么办,凉凉。
所以,上面的思路全是废话,从这里看起:
因为300*666(总和)数据并不算很大,可以直接用数组记录下来和,数组的第一个维度的大小就是300 * 666的两倍,两倍,因为有正负情况。另外还可以用滚动数组减小空间的使用,注意取mod

(下面有介绍滚动数组是个啥

状态转移方程:dp[i][maxn+j] = dp[i-1][maxn+j-a[i]]+dp[i-1][maxn-j];

看代码

#include<bits/stdc++.h>
using namespace std;
const double eps = 1e-6;
const int inf=1e9;
const int maxn=300*700;//比300*666略大就行 
const int mod = 1e8+7;
#define MAX_N 100007
#define MAX_M 10001
#define ll long long
int dp[2][maxn*2];
int a[301];
int main()
{
    #ifdef LOCAL
    freopen("test.txt","r",stdin);
    #endif
    int n;
    cin >> n;
    dp[0][maxn]=1;      //最开始是0,对应的是maxn+0,一开始; 
    for(int i=1;i<=n;i++){
    	scanf("%d",&a[i]);
    	for(int j=300*-666;j<=300*666;j++){      //j代表的是上一次加出来的和, 
    		if(j==666)
    		continue;
    		dp[i%2][maxn+j] = dp[i%2^1][maxn+j-a[i]]+dp[i%2^1][maxn-j];           //两种改变。 
    		if(dp[i%2][maxn+j]>mod){
    			dp[i%2][maxn+j] -= mod;
    		}
    	}
    }
    printf("%d\n",dp[n%2][maxn-666]);	

    return 0;
}

其他类似的题目:

同样是牛客练习赛的40场,A题
这题与上面是比较相似的,就是dp每一个音符,符合条件的加上,看牛客提交代码也能看到的懂。
那么既然看的懂为啥要看我这个呢,因为我这个有注释啊,那为啥我在这里不注释呢。因为现在说的不是这一题,欸,无懈可击。(如果有实在不懂的朋友可以在下方留言,我在有时间的时候可以更一起,虽然也是厚脸皮式的,人菜木得办法,T_T)

滚动数组是个啥

滚动数组简单理解就是通过不断循环利用数组,来减少空间的使用,这种思想其使用的还是比较多的。
最常见的有一种就是斐波那契数列的实现 :

 int a=1,b=1,c=a+b;
	for(int i=0;i<100;i++){
		a = b;
		b = c;
		c = a+b;
	}

斐波那契额的递推公式f[i] = f[i-1]+f[i-2];所以实际上需要的数只有三个,就是上面代码中的a,b,c。
那么有些朋友可能会说了:
“哦,我知道了,那上面这个状态转移方程(dp[i][maxn+j] = dp[i-1][maxn+j-a[i]]+dp[i-1][maxn-j];)也是有两个相加的,也可以变成a,b,c三个数”。
不行!,为啥呢,因为一维上的下标是不确定的,但是从二维角度上来看,得到下一行的所有数值,只需要前面一行的所有数就行了,所以可以压缩成只有两行的二维数组。不知道这样大家懂了没有。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值