【动态规划】K序列

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_38033475/article/details/79962046


思路

网赛的时候完全就瞎写,因为当时在想类比最大上升子序列,思路就很乱,最后写了个dfs也跑不动。果然动态规划能行,

但是由于思路限制(做dp的题做少了)也没想好。今天看的题解,豁然开朗。

两个数组:dp和temp规模都是k,因为我只需要子序列和模k的情况(结果为0~k)对应的最大长度,又是求“最值”,于是联想到动态规划。dp[j]表示算上当前的这个数值,结果为j的最大长度;temp[j]表示不算上当前的这个数值(因为最外对数组的每个元素进行遍历,相当于temp存储的是直到加到上一个元素时的情况),结果为j的最大长度。

那么显然状态转移方程为:

dp[(j+num[i])%k]=temp[j]+1;

然而还需要if语句,因为要考虑到:如果temp[j]==0就意味着之前都没有一个结果是j的,那么你dp就不应该在j的基础上去加当前这个数模k更新对应这个结果的长度,但是,j==0的时候(即一开始的时候 和 模k结果为0的情况第一次出现了)temp[j]==0,可我们需要用上。

综上,状态转移方程的条件:

if(j==0||temp[j]!=0)
至于temp数组的更新就不用说了吧,你每针对一个num数组的元素弄了一次,就该更新一次temp数组,让我知道结果为j的子序列最大长度是多少。

代码

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

const int maxn=1e5+5;
const int maxk=1e7+5;
int temp[maxk];
int dp[maxk];
int num[maxn];
int main()
{
	int n,k;
	cin>>n>>k;
	for(int i=0;i<n;i++)
		scanf("%d",&num[i]);
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<k;j++)
		{
			if(j==0||temp[j]!=0)
				dp[(j+num[i])%k]=temp[j]+1;
		}
		for(int j=0;j<k;j++)
			temp[j]=max(temp[j],dp[j]);
	}
	printf("%d\n",dp[0]);
} 

感受

这道题如果让我一开始来写我是无从下手的,因为我之前太纠结于“子序列--->可以不连续--->取当前元素或不取--->dfs???”,而这个动态规划,下标表示模k的结果,数值表示长度,主要是很巧妙地针对每一个num数组的元素,在内循环通过0~k的更新(“不管你行不行我先都要”),然后通过max函数来看看要了你这个元素之后能不能把我的最大长度提高,能的话我的temp数组跟着你刷新最大长度,不能的话我temp数组还是我原来的长度(“先斩后奏,数据决定最后要不要你”)。

或许这种“不管你行不行我先都要,先斩后奏,数据决定最后要不要你”是一种动态规划求最值的思想,是我需要理解的,而不同于很直观的在dfs里立马决定我要不要你。

觉得动态规划就是这样.....每次让我遇到就要讲那么多才能让自己觉得“哦!懂了!”。之后要多做题提高才行。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页