饭票 题解
1.题意简述
某天小
2.样例解释
3 10
1 2 4 2 1 1
8
样例中,我们有两张一元,一张两元和一张四元,可以凑出
3.思路
1.90分思路
我们先定义一个长度为
然后对于第
代码如下:
dp[0] = 1;
v.push_back (0);
For (i, 1, n) {
p = 0, len = v.size ();
for (int j = 0; j < len; j ++) {
int now = v[j];
for (int k = 1; k <= a[i].c; k ++) {
if (now + a[i].a * k > m) break;
if (dp[now + a[i].a * k] == 0) {
ans ++;
dp[now + a[i].a * k] = 1;
v.push_back (now + a[i].a * k);
}
}
}
}
cout << ans << endl;
2.100分思路
还是可以定义逻辑数组存储面值是否能凑到,还是将面值
如果新凑出的面值之前没有凑出过且在
我们新开一个
此外还要注意一点:即使这个点已经被凑出来,但代进去不满足上述方程,也需要更新一下。。。
3.代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
//define int long long
using namespace std;
#define N 100010
#define For(i,j,k) for(int i=j;i<=k;i++)
#define IOS ios::sync_with_stdio(),cin.tie(),cout.tie()
int n, m, ans = 0, dp[110][100010];
bool ok[N];
struct no {
int a, c;
}a[N];
int main () {
IOS;
cin >> n >> m;
For (i, 1, n) cin >> a[i].a;
For (i, 1, n) cin >> a[i].c;
ok[0] = true; //将面值 0 标记为true
For (i, 1, n) {
For (j, 0, m) { //从面值0枚举到面值m
if (ok[j] && dp[i][j] < a[i].c) {
//如果面值j已被凑出来过,且接下来凑出的面值确保使用不超过 c[i] 张
if (!ok[j + a[i].a] && j + a[i].a <= m) {
//如果接下来凑出来的面值之前没有凑出来过,并且在 1~m 的范围内
ans ++; //记录结果
ok[j + a[i].a] = true; //标记面值为true
dp[i][j + a[i].a] = dp[i][j] + 1;
//面值j使用的饭票数量是它派生出的面值的使用饭票数量+1
} else if (dp[i][j + a[i].a] > dp[i][j] + 1){
//如果已经被凑出来过,但是不满足方程
dp[i][j + a[i].a] = dp[i][j] + 1;
//维护一下数组,令其符合方程
}
}
}
}
cout << ans << endl; //输出
return 0;
}
4.一个可以有的小优化
事实上,在循环时
下面是开了二维与不开二维的区别 (
开了二维:
不开二维:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现