当前位置: 首页 > news >正文

背包DP总结

牛客周赛 Round 81 E.建筑入门

在这里插入图片描述

  • 知识点:完全背包,完全背包的路径转移以及回溯

由题意可以推导出,下层麻将的数字一定大于上层数字,所以我们可以假设一个最基础的麻将塔,也就是:
1
2 2
3 3 3

形如这样的,然后再从下往上一层一层加数字,且得保证下层一定得大于上层,所以可以想象成,如果倒数第二层加数字的话,倒数第一层也得加数字,那就可以翻译成 选择一个从第i层向下的后缀层数,每一层每一个块均+1,然后选择若干这样的后缀和,最后使得总值正好等于k。

这也就是转化成了一个经典的完全背包问题。
f[i][j] 表示 前i个数字里面选择之后数字和恰好为j能否做到,记录的是一个bool类型的值

但是还有个问题:如何记录路径?
学过01背包的小伙伴肯定都知道01背包记录路径的方法,只需要记录选择了哪件物品即可,但是介于完全背包每个物品可以选择很多件,所以不能单单记录选择了哪件物品,这就需要记录我们的背包体积是什么时候发生变化的。

vector<PII> path(k + 1);  // 记录一下当前体积为 i 的上一步体积是多少

一些回溯包括其他的细节大家参考代码理解吧

#include<bits/stdc++.h>

using namespace std;
using i64 = long long;
using u64 = unsigned long long;

#define int long long
#define debug(x) cerr << #x" = " << x << '\n';

typedef pair<int, int> PII;

const i64 N = 2e5 + 10, INF = 1e18 + 10;

void solve()
{
    int n, k;
    cin >> n >> k;
    k = k - n * (n + 1) * (2 * n + 1) / 6;  // 1^1 + 2^2 + 3^3 + ... + n^n
    if (k < 0) {
        cout << -1 << endl;
        return;
    }
    vector<int> a(n + 2);
    for (int i = n; i > 0; i--) {
        a[i] = a[i + 1] + i;
    }
    vector<vector<int>> f(n + 1, vector<int>(k + 1, 0));  // 前i个数字里面选择之后数字和恰好为j能否做到
    vector<PII> path(k + 1);  // 记录一下当前体积为 i 的上一步体积是多少
    f[0][0] = 1;
    for (int i = 1; i <= n; i++) {  // (以倒数第n行为开始的后缀)(倒数第n - 1行开始的)。。
        for (int j = 0; j <= k; j++) {
            f[i][j] = f[i - 1][j];
            if (j - a[i] >= 0 && f[i][j - a[i]]) {
                f[i][j] = 1;
                path[j] = {j - a[i], i};
                // 体积为 j 的上一步是 j - a[i];
                // 把第i号物品拿过来之后进行的转移
            }
        }
    }
    if (!f[n][k]) {
        cout << -1 << endl;
        return;
    }
    vector<int> nums;
    int curr = k;
    while(curr > 0) {
        auto [pre, x] = path[curr];
        nums.emplace_back(x);
        curr = pre;
    }
    vector<int> ans(n + 1, 0);
    for (auto &x : nums) {
        ans[x]++;
    }
    for (int i = 1; i <= n; i++) {
        ans[i] += ans[i - 1];
    }
    for (int i = 1; i <= n; i++) {
        ans[i] += i;
        cout << ans[i] << " \n"[i == n];
    }
}

signed main()
{
    cin.tie(nullptr) -> ios::sync_with_stdio(false);
    int T = 1;
    // cin >> T;
    while (T --) solve();
    return 0;
}

牛客周赛 Round 86 E 小苯的Polygon

在这里插入图片描述

  • 知识点:01背包,状态能否成功表示

本来都已经想到状态表示了,结果转移觉得写起来不太对劲就放弃了。

f [ i ] [ j ] f[i][j] f[i][j] 表示 从前i个木棍中选择,多边形的边长为 j 的这种方案可行不可行.

经典01背包转移,但是有一个问题,如何保证所选的木棍一定能构成一个多边形呢?

首先我们可以发现一个性质,能构成多边形的木棍一定满足最长的木棍的长度大于所有其他木棍的长度之和,满足这个条件的已经就可以构成多边形。

所以我们在转移的时候,要先枚举a[i] 作为最大值,然后从 a[i] ~ Max(相当于枚举其余木棍的和)进行转移,只有这样,才能满足构成多边形的条件。

为了保证 a[i] 都是最大值,我们选择排序之后枚举。

但是还有一个问题,就是会不会存在类似于只有两个木棍,满足上述条件,然后进行了错误转移呢??(考虑会不会存在木棍少,构不成多边形的情况)。

答案是不会,因为我们排完序后,前面的木棍长度一定严格小于后面,所以 a[i] ~ Max 这个集合中一定都是大于等于2个木棍的情况。
譬如 1 2 3, 我们呢发现对于这种情况,枚举到 3 的时候前缀和只有 3,则不能进行转移。

void solve()
{
	int n;
	cin >> n;
	vector<int> a(n + 1);

	int Max = -1;
	for (int i = 1; i <= n; i++) cin >> a[i], Max = max(a[i], Max);
	sort(a.begin() + 1, a.end());

	vector<vector<int>> f(n + 1, vector<int>(n * Max + 1, 0));
	int ans = INF;
	f[0][0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = a[i] + 1; j <= n * Max; j++) {
			if (f[i - 1][j]) {
				ans = min(ans, a[i] + j);
				break;
			}
		}
		f[i] = f[i - 1];
		for (int j = a[i]; j <= Max * n; j++) {
			f[i][j] |= f[i - 1][j - a[i]];
		}
	}
	// for (int i = 1; i <= n; i++) {
	// 	for (int j = 1; j <= Max * n; j ++) {
	// 		cout << f[i][j] << ' ';
	// 	}
	// 	cout << endl;
	// }

	if (ans > 1e17) ans = -1;
	cout << ans << endl;
}

http://www.dtcms.com/a/106621.html

相关文章:

  • GO语言 使用protobuf
  • 【第十三届“泰迪杯”数据挖掘挑战赛】【2025泰迪杯】【代码篇】A题解题全流程(持续更新)
  • 全国产ADC 16bit 2通道1G采样 双FMC子板
  • C++多继承
  • 【抓包工具】win 10 / win 11:Charles 下载、安装、配置(快捷方式、默认端口、登录、https 证书)
  • 【git】VScode修改撤回文件总是出现.lh文件,在 ​所有 Git 项目 中全局忽略特定文件
  • MacOS 的 AI Agent 新星,本地沙盒驱动,解锁 macOS 操作新体验!
  • 地表-地下水系统交互模拟关键技术突破:SWAT-MODFLOW耦合模型构建、验证及多情景预测研究
  • 离线语音识别 ( 小语种国家都支持)可定制词组
  • 项目管理管什么?理什么?
  • 《云端都市:云计算如何重塑未来城市形态》
  • spikingjelly:使用单层全连接 SNN 识别 MNIST
  • Java UnsupportedOperationException 深度解析及解决方案
  • 在HarmonyOS NEXT 开发中,如何指定一个号码,拉起系统拨号页面
  • Python从入门到精通4:计算机网络及TCP网络应用程序开发入门指南
  • JuiceFS vs HDFS,最简单的 JuiceFS 入门
  • Muduo网络库实现 [八] - Acceptor模块
  • 【Harmony OS】TypeScrip基础
  • 小米汽车就 SU7 事故回应六点问题,称「事故车起火并非自燃」、「无法分析车门能否打开」,如何看待?
  • 从头开发一个Flutter插件(二)高德地图定位插件
  • [GESP 202503 二级 T2] 时间跨越
  • Docker 镜像导出与导入:export/import vs save/load
  • AI战略群与星际之门:软银AI投资版图计划深度解析
  • AI辅助下基于ArcGIS Pro的SWAT模型全流程高效建模实践与深度进阶应用
  • deepseek-r1 api部署和镜像
  • OpenCV 图形API(9)用于执行矩阵与标量之间的逐元素除法操作函数divC()
  • 获取oracle表大小
  • 《系统分析师开篇》
  • 电动打气泵方案,多种充气模式的充气泵方案【天吉智芯】
  • 从商汤科技年报,看一家AI企业的确定性叙事