一本通1299 糖果

题目传送门

思路

首先一眼有了一个状态:设\(f_{i,j}\)表示前\(i\)个数能否得到\(j\),如果可以那么就是\(1\),反之为\(0\)。这样状态转移方程也非常好写:\(f_{i+1,j+a_i}=max(f_{i+1,j+a_i} , f_{i,j})\),然后再注意一下状态的继承即可。

但是这样设计状态明显是被空间和时间限制的,那么到底应该怎样设计状态呢?首先我们可以发现,上个状态的设计跟\(k\)的取值其实毫无关联,但是实际\(k\)的范围很小,所以我们相当于浪费了一个条件。由于\(k\)的范围很小,而最后的答案又要整除\(k\)。所以可以设\(f_{i,j}\)表示前\(i\)个数除以\(k\)的余数是\(j\)时的最大答案。那么最后输出\(f_{n,0}\)即可。中间状态转移方程也很简单,一个个数往前递推即可。但是注意细节就是一定要继承之前已经找到的状态,即\(f_{i,j}=max(f_{i,j},f_{i-1,j})\),用语言来描述就是前\(i-1\)个数能找到的最大值,在前\(i\)个数中一定也能找到。

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
int n,k;
int a[110];
int f[110][110];
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	memset(f,-1,sizeof(f));
	for(int i=0;i<=n;i++){
		f[i][0]=0;//别忘了初始化
	}
	for(int i=0;i<=n;i++){
		for(int j=0;j<k;j++){
			if(i>0) f[i][j]=max(f[i][j],f[i-1][j]);//细节继承前面的状态
			if(f[i][j]!=-1){
				f[i+1][(j+a[i+1])%k]=max(f[i+1][(j+a[i+1])%k],f[i][j]+a[i+1]);//状态转移方程
			}
		}
	}
	printf("%d\n",f[n][0]);
	return 0;
}
posted @ 2021-02-19 15:47  徐明拯  阅读(234)  评论(0编辑  收藏  举报