题目描述:
- 有nnn中大小不同的数字aia_iai,每种数字mim_imi,判断是否可以从这些数字之中选出若干使它们的和恰好为KKK。
限制条件:
- 1≤n≤1001≤n≤1001≤n≤100
- 1≤ai,mi≤1000001≤a_i,m_i≤1000001≤ai,mi≤100000
- 1≤K≤1000001≤K≤1000001≤K≤100000
题解:
一般动态规划:
- 步骤:
- dp数组含义:dp[i][j]dp[i][j]dp[i][j]=用前iii种数字是否能拼成jjj
- 初始条件:dp[0][1−K]=False;dp[0−n][0]=Turedp[0][1-K]=False; dp[0-n][0]=Turedp[0][1−K]=False;dp[0−n][0]=Ture
- 递推公式:dp[i][j]={∃dp[i−1][j−k×ai]为真∣0≤k≤mi&k×ai≤j}dp[i][j]=\{∃ dp[i-1][j-k×a_i ]为真|0≤k≤m_i \& k×a_i≤j\}dp[i][j]={∃dp[i−1][j−k×ai]为真∣0≤k≤mi&k×ai≤j}
- 递推方向:从上向下
- 结果:dp[n][K]dp[n][K]dp[n][K]是否为真
- 时间复杂度 :O(K∑imi)O(K∑_im_i )O(K∑imi)
- 代码 :函数 solve1()solve1()solve1()
优化动态规划:
- 上面的动态规划的优化方向:
- 第一: dp数组中只存取bool型结果会有不少浪费,可以用来存取更有效的信息
- 第二:我们递推只使用用了从上到下的递推方向,我们可能可以使用左到右的递推方向来辅助
- 步骤:
- dp数组含义:dp[i][j]dp[i][j]dp[i][j]=用前iii种数字是拼成jjj,第i种数字最多剩余多少个,如果拼不成j就令其为-1
- 初始条件:dp[0][1−K]=−1;dp[0][0]=0,dp[1−n][0]=midp[0][1-K]=-1; dp[0][0]=0,dp[1-n][0]=m_idp[0][1−K]=−1;dp[0][0]=0,dp[1−n][0]=mi
- 递推公式:dp[i][j]={midp[i−1][j]≥0−1j<aiordp[i][j−ai]≤0dp[i][j−ai]−1其他dp[i][j]=\begin{cases} m_i & dp[i-1][j]≥0 \\ -1 &j<a_i or dp[i][j-a_i ]≤0\\ dp[i][j-a_i ]-1 &其他\\\end{cases}dp[i][j]=⎩⎪⎨⎪⎧mi−1dp[i][j−ai]−1dp[i−1][j]≥0j<aiordp[i][j−ai]≤0其他
- 递推方向:从上向下,从左到右
- 结果:dp[n][K]dp[n][K]dp[n][K]是否为真
- 时间复杂度 :O(nK)O(nK )O(nK)
- 代码 :函数 solve2()solve2()solve2()
代码:
#include <iostream>
#define Max_N 105
#define Max_K 100005
using namespace std;
int n,K;
int a[Max_N],m[Max_N];
bool dp[Max_N][Max_K];
int Dp[Max_N][Max_K];
int DP[Max_K];
void solve1()
{
for(int i=1; i<=K; i++)
dp[0][i]=false;
for(int i=0; i<=n; i++)
dp[i][0]=true;
for(int i=1; i<=n; i++)
for(int j=1; j<=K; j++)
for(int k=0; k<=m[i]&&k*a[i]<=j; k++)
dp[i][j]|=dp[i-1][j-k*a[i]];
if(dp[n][K])
cout<<"Yes"<<endl;
else
cout<<"NO"<<endl;
}
void solve2()
{
for(int i=1; i<=K; i++)
Dp[0][i]=-1;
for(int i=1; i<=n; i++)
Dp[i][0]=m[i];
Dp[0][0]=0;
for(int i=1; i<=n; i++)
for(int j=1; j<=K; j++)
{
if(Dp[i-1][j]>=0)
Dp[i][j]=m[i];
else if(j<a[i]||Dp[i][j-a[i]]<=0)
Dp[i][j]=-1;
else
Dp[i][j]=Dp[i][j-a[i]]-1;
}
if(Dp[n][K]>=0)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
void solve3()
{
for(int i=1; i<=K; i++)
DP[i]=-1;
DP[0]=0;
for(int i=1; i<=n; i++)
for(int 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)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
int main()
{
cin>>n>>K;
for(int i=1; i<=n; i++)
cin>>a[i]>>m[i];
solve1();
solve2();
solve3();
return 0;
}