P1622 释放囚犯 题解

P1622 释放囚犯 题解

简化题意

一个监狱中有 \(P\) 个牢房,每个牢房中都有一个犯人,每个犯人都处于与他编号相同的牢房中。
现在需要释放 \(Q\) 个犯人,第 \(i\) 个表示监狱将释放犯人 \(a_i\)。

而当犯人 \(a_i\) 被释放时,所有与犯人 \(a_i\) 直接相邻(没有空牢房间隔)的犯人都会得到消息,为了安抚他们的情绪,看守需要给所有得到消息的犯人吃一块肉。
看守可以随意安排释放的 \(Q\) 个犯人的顺序,请安排一种顺序,使看守需要花费的肉的块数最少。

解题思路

先观察一下犯人出狱的规律,我们模拟一下样例:

P = 20, Q = 3
a = {3 6 14}

假设按照给定的顺序释放犯人:

  • 先释放 \(3\) 号犯人,此时 \(1 \sim 2\) 号犯人、\(4 \sim 20\) 号犯人各得到一块肉,消耗 \(19\) 块肉。
  • 再释放 \(6\) 号犯人,此时 \(4 \sim 5\) 号犯人、\(7 \sim 20\) 号犯人各得到一块肉,消耗 \(16\) 块肉。
  • 最后释放 \(14\) 号犯人,此时 \(7 \sim 13\) 号犯人、\(15 \sim 20\) 号犯人各得到一块肉,消耗 \(13\) 块肉。
    总计消耗了 \(19 + 16 + 13 = 48\) 块。

这时候,我们能够观察到一个性质:把整个监狱看作一个数组,那么每一个犯人出狱,都相当于在这个数组中打了一个断点,那么实际上这题就使整个区间中,逐渐变得越来越小。
而这时候,我们就注意到:这里的区间是越变越小的,而区间 DP 的思路是从小区间逐渐扩展到大区间,无法操作。

这里我们就要开动脑筋了,利用倒推了:假设我们这 \(Q\) 个牢房都是空的,而我们要安排这 \(Q\) 个囚犯按任意顺序入住。
当囚犯 \(a_i\) 入住时,所有与其联通的牢房中的囚犯都会给他一块肉。求最小化囚犯给出的肉的数量。

这样,问题就变会了从小区间扩展到大区间,可以利用区间 DP 来操作了。

我们定义 \(dp_{i, j}\) 为将 \([i, j]\) 合并为完整区间,所需的最少肉。这里 \(i, j\) 都是指将入住的犯人的编号,而非数组下标。
这样,我们能推出状态转移方程:\(dp_{i, j} = dp_{i, k - 1} + dp_{k + 1, j} + a_{j + 1} - a_{i - 1} - 1 - 1\)
初始有 \(f_{i, i} = a_{i + 1} - a_{i - 1} - 1 - 1\)。

这样,我们就可以求出最小所需肉的块数了。

代码部分

#include <bits/stdc++.h>
using namespace std;

int p, q;
int a[110], dp[1010][1010];

int main()
{
	scanf("%d %d", &p, &q);
	for(int i = 1;i <= q;i ++)
		scanf("%d", &a[i]);
	a[q + 1] = p + 1;
	for(int len = 1;len <= q;len ++)
		for(int i = 1;i + len - 1 <= q;i ++)
		{
			int j = i + len - 1;
			dp[i][j] = 1e9;
			for(int k = i;k <= j;k ++)
				dp[i][j] = min(dp[i][j], dp[i][k - 1] + dp[k + 1][j] + a[j + 1] - a[i - 1] - 1 - 1);
		}
	
	printf("%d\n", dp[1][q]);			
	return 0;
}

posted on 2026-10-06 16:44  Leo_29  阅读(7)  评论(0)    收藏  举报

导航