动态规划问题,我们这里定义dp[i][j]为bool类型,其意义表示为用前i种数(下标为0~i-1)能否凑成和j,能的话则dp[i][j]=true,反之则dp[i][j]=false,那么根据其前一种状态dp[i-1]j-k*a[i-1],如果dp[i-1][j-k*a[i-1]]=true,那么对于第i个数a[i-1]我们只要再用k个,那么就可以用前i个数凑成和为j。这是很容易理解的。递推关系式为:
代码如下:
#include<iostream>
using namespace std;
#define Max_N 100
#define Max_K 100000
bool dp[Max_N+1][Max_K+1];
int a[Max_N];
int m[Max_N];
int n,K;
void solve()
{
dp[0][0]=true;
for(int j=1;j<=K;j++)
dp[0][j]=false; //用前0种数凑成和不为0的,肯定不行
//dp[i][j] 用前i种数(下标0~i-1)能不能凑成和为j
for(int i=1;i<=n;i++)
{
for(int j=0;j<=K;j++)
{
//下标为i-1的数使用k次
for(int k=0;k<=m[i-1] && k*a[i-1]<=j;k++)
{
//不断或等
dp[i][j] |= dp[i-1][j-k*a[i-1]];
}
}
}
if(dp[n][K]) cout<<"Yes\n";
else cout<<"No\n";
return ;
}
int main()
{
cin>>n>>K;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
for(int i=0;i<n;i++)
{
cin>>m[i];
}
solve();
}
其实大家也能看到这个程序的缺点就在于三重循环下,效率还是比较低,那么我们就想着有没有更高效的方法,那么我们就可以使用另一种定义方法dp[i][j]表示:用前i种数(下标0~i-1)在能凑成和为j时,第i个数(下标为i-1)最多可以剩余多少个,-1表示前i种数凑不成和为j,递推关系式如下(每段的意义我将会写在程序的注释中):
#include<string.h>
#include<algorithm>
#include<iostream>
#define maxn 100000
using namespace std;
int n,k;
int a[maxn+5],m[maxn+5];
int dp[105][maxn+5];
void solve()
{
fill(dp[0],dp[0]+maxn+5,-1); //前0种和凑成和不为0的,肯定不行
dp[0][0]=0;
/*dp[i][j] 用前i种数(下标0~i-1)在能凑成和为j时,第i个数(下标为i-1)
最多可以剩余多少个,-1表示前i种数凑不成和为j */
for(int i=1;i<=n;i++){
for(int j=0;j<=k;j++){
//如果前i-1个数能和成j则第i个数a[i-1]剩余为m[i-1]个
if(dp[i-1][j]>=0)dp[i][j]=m[i-1];
//前i-1种数凑不成和为j,那么就用第i个数a[i-1]尝试凑
//如果要凑成的和j<a[i-1],肯定是凑不成j的
//如果在用前i个数能凑成和j-a[i-1],但是这时候a[i-1]数用完了,那么也是凑不成的
else if(j<a[i-1]||dp[i][j-a[i-1]]<=0)dp[i][j]=-1;
//在前i种数能凑成和j-a[i-1]并且数a[i-1]还有剩余,那么是能凑成的,再用掉一个a[i-1],所以个数减一
else dp[i][j]=dp[i][j-a[i-1]]-1;
}
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=0;i<n;i++)
scanf("%d",&m[i]);
solve();
if(dp[n][k]>=0)printf("Yes\n");
else printf("No\n");
return 0;
}