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 行:
第一行有两个整数 PQ
第二行有 Q 个整数,表示 a_1,a_2...a_Q
1P10000,1Q100
输出
输出 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;
}

我的视频题解空间

posted on   itdef  阅读(39)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 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内存管理

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示