例
有n种不同大小的数字ai ,每种有mi 个,判断是否可以从这些数字中选出若干使他们的和恰好为k
样例输入
n=3
a
i
a_i
ai,
m
i
m_i
mi
3 3
5 2
8 2
k=17
输出
YES (3*3+8=17)
思路
同样是用基本的dp来求解,一般遇到这种问题,我们首先要找到他的递推式
我们假定dp【i】【j】表示 “用前 i 种数字加和得到j时还剩多少个m【i】,如果不能得到则取-1”
因此我们得到递推式
1.当dp【i-1】【j】>=0时,代表前i-1个数字已经可以加和成j ,第i个数字不用选也能成立 所以 dp【i】【j】=m【i】;
2.当 j<a【i】或者 dp【i】【j-a[i]】<0时,代表当前j不够选a【i】或者选了a【i】后并不能加和成j ,所以此时dp【i】【j】= -1;
3.最后只剩下可以选择 a【i】的情况,因此在这个情况下选择后 ,
dp【i】【j】=dp【i】【j-a【i】】-1 (对应于选择前对应a【i】数量-1)
找着这个对应关系很快就能写出对应代码。
而如果想要更加简化代码(非必要),我么可以将dp数组中的i省略降为一维数组
代码如下
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
int main()
{
int dp[100];
int n,i,t,j,k;
int a[100],m[100];
memset(dp,-1,sizeof(dp));
dp[0]=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i],&m[i]);
}
scanf("%d",&k);
for(i=1;i<=n;i++)
for(j=0;j<=k;j++)
{
if(dp[j]>=0)
{
dp[j]=m[i];
}
else if(j<a[i]||dp[j-a[i]]<0)
{
dp[j]=-1;
}
else
{
dp[j]=dp[j-a[i]]-1;
}
}
if(dp[k]>=0)
{
printf("YES");
}
}
为何省略i后与原来一样 ?
因为每一次在新的循环,dp【j】对应的数值是在i-1基础上处理后得到的数值
我们求出在i的基础上处理的数值后直接存储进dp【j】,而再之后i-1对应的数值不再会用到了,被取代也没有关系。