CF687C Solution

题目链接

题解

数据范围可知是一道dp题。思考时发现,在转移时需要上一个状态能得出的价值,但存储每个状态的所有价值并不可行。这时注意到可得价值\(\le k\)\(k\le 500\),可以用可行性dp判断每一个价值。

状态:\(dp[i][j][p]\)表示前\(i\)个数中选出和为\(j\)的数是/否(0/1)可以得出价值\(p\)

初始值:\(dp[0][0][0]=1\)

转移方程:

\[dp[i][j][p]|=dp[i-1][j][p]\\ dp[i][j][p]|=dp[i-1][j-c_i][p]\quad (j\ge c_i)\\ dp[i][j][p]|=dp[i-1][j-c_i][p-c_i]\quad (j\ge c_i,p\ge c_i)\\ (1\le i\le n,0\le j\le k,0\le p\le j) \]

第1行为不选\(c_i\)的情况。第2行为选\(c_i\)但不用其凑价值\(p\)的情况。第3行为选\(c_i\)并用其凑价值\(p\)的情况。

答案:\(q=\sum\limits_{i=0}^k dp[n][k][i]\)\(values=i(0\le i\le k,dp[n][k][i]=1)\)

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=501;
int c[N];
bool dp[N][N][N];
int main()
{
	int n,k,ans=0;
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&c[i]);
	dp[0][0][0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=k;j++)
		{
			for(int p=0;p<=j;p++)
			{
				dp[i][j][p]|=dp[i-1][j][p];
				if(j>=c[i]) dp[i][j][p]|=dp[i-1][j-c[i]][p];
				if(j>=c[i] && p>=c[i]) dp[i][j][p]|=dp[i-1][j-c[i]][p-c[i]];
			}
		}
	}
	for(int i=0;i<=k;i++) ans+=dp[n][k][i];
	printf("%d\n",ans);
	for(int i=0;i<=k;i++)
		if(dp[n][k][i]) printf("%d ",i);
	return 0;
}
原文地址:https://www.cnblogs.com/violetholmes/p/14388359.html