1 条题解
-
1方乐行 (2022ts124) LV 7 @ 2023-3-8 21:59:46
这道题其实挺简单的
这道题一看就是dp题,但是标签里还打了模拟,搜索,贪心?我的是完全背包在加上一个数组来记录已经使用的个数,这道题不能算是一个多重背包,因为多重背包是每个物品都有数量限制,而这道题只有总数量限制,我们在正常递推的过程中加上总数量和能否成功组合的判断就行一,划分阶段
以总面额和面额种类划分
二,确定状态变量
表示有多少种方法可以组合出面值,但是如果光有数组可不行,因为邮票的总数有限制,所以我们增加一个数组表示used,也就是组出面值所需要的最小邮票数
三,状态转移方程
数组的状转特别简单,可以通过求得,其中表示第种邮票的面额,状转为
dp[i]+=dp[i-a[j]]
而的状态可以在和中取最小值转移,那我们需要通过memset
来初始化数组,memset是通过字节赋值,如果我们在第二个参数填0x3f
,那么将会赋一个极大值,0x7f
是最大,讲完初始化我们的状转是u[i]=min(u[i],u[i-a[j]]+1)
四,设置边界
dp[0]=1
u[0]=0
五,ACCODE
#include<bits/stdc++.h> using namespace std; int k,n,a[51],dp[10000001],u[10000001]; int main() { cin>>k>>n; for(int i=1;i<=n;i++) { cin>>a[i]; } dp[0]=1; memset(u,0x3f,sizeof(u));//为了比最小值,要赋一个极大值 u[0]=0; int i; for(i=1;;i++)//第二个参数不填就会一直只需,所以要借助break { if(u[i-1]>k)//如果上一个的使用数量超过限制,就算失败 { i-=2;//上一个不行,最终结果就-2 break; } for(int j=1;j<=n;j++) { if(a[j]<=i)//不加这个判断会越界 { dp[i]=dp[i-a[j]]; u[i]=min(u[i],u[i-a[j]]+1);//状态转移 } } if(!dp[i])//没有求出i的组合,答案算上一个 { i--; break; } } cout<<i;//i也相当于最多连续的数量 }
等一等,先别划走,还可以优化,首先观察题目,可以发现状态数组只需要开2000001个,不需要开10000001个, 然后dp其实只需要1和0两种状态就够,开 接着对的判断也不需要下一轮,直接和对的判断合在一起
LATEST ACCODE
#include<bits/stdc++.h> using namespace std; int k,n,a[51],u[2000001]; bool dp[2000001];//降低空间复杂度 int main() { cin>>k>>n; for(int i=1;i<=n;i++) { cin>>a[i]; } dp[0]=1; memset(u,0x3f,sizeof(u)); u[0]=0; int i; for(i=1;;i++) { for(int j=1;j<=n;j++) { if(a[j]<=i) { dp[i]+=dp[i-a[j]]; u[i]=min(u[i],u[i-a[j]]+1); } } if(!dp[i]||u[i]>k)//将两个判断合并 { i--; break; } } cout<<i; }
- 1
信息
- ID
- 589
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 7
- 标签
- 递交数
- 54
- 已通过
- 14
- 上传者