papamelon 255. 贿赂囚犯 Bribe the Prisoners
https://www.papamelon.com/problem/255
一个监狱里有 P 个并排着的牢房, 从左到右依次编号为 1,2,...P
最初所有的牢房里都住着一个囚犯。相邻的两个牢房之间有一个窗户, 可以通过它与相邻的牢房里的囚犯对话。
现在要释放一批囚犯, 如果释放某个牢房的囚犯, 其相邻的牢房里的囚犯就会知道, 因而发怒暴动。
所以释放某个牢房的囚犯时, 必须要贿赂两旁相邻的囚犯一枚金币。
另外为了防止释放的消息在相邻牢房传开, 不仅两旁直接相邻的牢房, 所有可能听到消息的囚犯,
即直到空牢房或者到监狱两端为止, 此间的所有囚犯都必须给一枚金币。
现在要释放 a_1,a_2...a_Q牢房里的 Q 名囚犯, 释放的顺序还未确定。
如果选择所需金币数尽量少的顺序释放, 最少需要多少金币?
输入
第一行整数 T(1≤T≤100),表示有多少组测试数据
每组测试数据有 2 行:
第一行有两个整数 P 和 Q
第二行有 Q 个整数,表示 a_1,a_2...a_Q
1≤P≤10000,1≤Q≤100
输出
输出 T 行,每行格式:Case #{第几组测试数据}: {答案},表示第几组测试数据,答案是多少
样例 1
输入
1
20 3
3 6 14
输出
Case #1: 35
如图
假如我们释放14号牢房囚犯,那么计算当前需要的花费 ,再加上释放1到13号房间中的3号和6号的囚犯的最小花费,就可以得到答案1
假如我们释放3号牢房囚犯,那么计算当前需要的花费 ,再加上释放4到20号房间中的14号和6号的囚犯的最小花费,就可以得到答案2
假如我们释放6号牢房囚犯,那么计算当前需要的花费 ,再加上释放1到5号房间中的3号囚犯的最小花费,再加上释放7到20号房间中的14号囚犯的最小花费,就可以得到答案3
答案123中的最小花费就是最后的实际答案。
我们可以使用递归来计算 使用dp[x][y]记录 释放x到y之间所有囚犯的最小开销。 但是由于P的范围太大,数组内存过大,这里使用哈希表记录释放x到y之间所有囚犯的最小开销。
用来避免重复计算.
代码
#include <iostream>
#include <algorithm>
#include <memory.h>
#include <unordered_map>
using namespace std;
int t, p, q;
const int N = 10010;
int arr[N];
unordered_map<long long, int > dp;
int dfs(int rooml, int roomr, int arrl, int arrr) {
if (roomr < rooml) return 0;
if (arrr < arrl) return 0;
long long key = rooml + roomr * 100000;
if (dp.count(key) != 0) return dp[key];
//if (dp[rooml][roomr] != 0x3f3f3f3f) return dp[rooml][roomr];
int ans = 0x3f3f3f3f;
for (int i = arrl; i <= arrr; i++) {
int selRoom = arr[i];
int c = (selRoom - rooml);
int d = (roomr - selRoom);
int a = dfs(rooml, selRoom - 1, arrl, i - 1);
int b = dfs(selRoom + 1, roomr, i + 1, arrr);
ans = min(ans, a + b + c + d);
}
dp[key] = ans;
return ans;
}
void solve(int curr) {
dp.clear();
cin >> p >> q;
for (int i = 1; i <= q; i++) {
cin >> arr[i];
}
sort(arr + 1, arr + 1 + q);
int ans = 0x3f3f3f3f;
for (int i = 1; i <= q; i++) {
int selRoom = arr[i];
int c = (selRoom - 1);
int d = (p - selRoom);
int a = dfs(1, selRoom - 1, 1, i - 1);
int b = dfs(selRoom + 1, p, i + 1, q);
ans = min(ans, a + b + c + d);
}
cout << "Case #" << curr << ": " << ans << endl;
}
int main()
{
cin >> t;
for (int i = 1; i <= t; i++) {
solve(i);
}
return 0;
}
下面再来一个dp数组优化内存版本的方案
使用dp[x][y]记录 释放x到y房间之间的所有囚犯的最小开销。 但是由于P的范围太大,数组内存过大.无法使用这个数组.
使用arr[N]的数组记录要释放的囚犯号码, 可以考虑dp[x][y]表示释放arr[x]~~arr[y]之间的所有囚犯的最小开销。
dp[x][y] = min(dp[x][x+1]+dp[x+1][y] , dp[x][x+2],dp[x+2][y] ... ,dp[x][y-1]+dp[y-1][y]);
#include <iostream>
#include <memory.h>
#include <algorithm>
using namespace std;
const int N = 110;
int arr[N];
int dp[N][N];
int t, n, l;
int dpFunc(int l, int r) {
if ( r <=l+1) {
dp[l][r] = 0;
}
if (dp[l][r] != -1) return dp[l][r];
int ans = 0x3f3f3f3f;
for (int i = l + 1; i < r; i++) {
ans = min(ans, (arr[r] - arr[i]-1) + (arr[i]-arr[l]-1) + dpFunc(l, i) + dpFunc(i, r));
}
dp[l][r] = ans;
return ans;
}
void solve(int currIdx) {
cin >> l >> n;
memset(arr, 0, sizeof arr);
for (int i = 1; i <= n; i++) {
cin >> arr[i];
}
//sort(&arr[1],&arr[1]+n);
arr[0] = 0; arr[n + 1] = l + 1;
memset(dp, -1, sizeof dp);
int ans = dpFunc(0, n + 1);
cout << "Case #" << currIdx << ": " << ans << endl;
return ;
}
int main() {
cin >> t;
for (int i = 1; i <= t; i++) {
solve(i);
}
return 0;
}
作 者: itdef
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力


【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
2021-09-04 Leetcode 215. 数组中的第K个最大元素 排序 优先队列
2020-09-04 LeetCode 257. 二叉树的所有路径 dfs
2019-09-04 acwing 76. 和为S的连续正数序列
2019-09-04 leetcode 19 删除链表的倒数第N个节点
2019-09-04 水文一篇 汇报下最*的学**况
2018-09-04 muduo 的windows下的编译
2016-09-04 操作系统学习笔记(三) windows内存管理