分析:拿到这道题目,当然可以用递归的形式进行深度搜索遍历每种情况,但是效率会非常低。一般递归带有前后数值关系的递归是可以用动态规划转化的,效率会提高很多。
至于动态规划,最重要的就是准确定义动态数组的意义并找准前后的递推式.我们这里定义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]](0<=k<=m[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<iostream>
using namespace std;
#define Max_N 100
#define Max_K 100000
int dp[Max_N+1][Max_K+1];
int a[Max_N];
int m[Max_N];
int n,K;
void solve()
{
dp[0][0]=0;
for(int j=1;j<=K;j++)
dp[0][j]=-1; //用前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++)
{
if(dp[i-1][j]>=0)
//如果前i-1种数就能凑成和为j,那么第i个数a[i-1]可以一个都不用,全部剩下来
dp[i][j]=m[i-1];
else //前i-1种数凑不成和为j,那么就用第i个数a[i-1]尝试凑
{
//如果要凑的和j小于a[i-1],那么肯定凑不成j ------ j<a[i-1]
//无法用前i个数凑不成和j-a[i-1],那么无论再加不加上一个数a[i-1],都凑不成和j -----dp[i][j-a[i-1]<0
//如果在用前i个数能凑成和j-a[i-1],但是这时候a[i-1]数用完了,那么也是凑不成的 ------dp[i][j-a[i-1]=0
if(j<a[i-1] || dp[i][j-a[i-1]]<=0)
dp[i][j]=-1;
else
//在前i种数能凑成和j-a[i-1]并且数a[i-1]还有剩余,那么是能凑成的,再用掉一个a[i-1],所以个数减一
dp[i][j]=dp[i][j-a[i-1]]-1;
}
//cout<<"dp["<<i<<"]["<<j<<"]:"<<dp[i][j]<<endl; //测试使用
}
}
//dp[n][K]>=0表明前n个数(a[0]~a[n-1])在凑成和为K时,数a[i-1]不剩余或者还有剩余,
//但是不管剩余还是不剩余,都是能凑成和为j
if(dp[n][K]>=0) 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();
}
PS:至于动态规划的题目,重点在于定义好动态数组的意义和找准递推式,这个没办法,这个的话就只能多做题多感悟了