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;
}