题目描述
Caima 王国中有一个奇怪的监狱,这个监狱一共有 P P P 个牢房,这些牢房一字排开,第 i i i 个紧挨着第 i + 1 i+1 i+1 个(最后一个除外)。现在正好牢房是满的。
上级下发了一个释放名单,要求每天释放名单上的一个人。这可把看守们吓得不轻,因为看守们知道,现在牢房中的 P P P 个人,可以相互之间传话。如果某个人离开了,那么原来和这个人能说上话的人,都会很气愤,导致他们那天会一直大吼大叫,搞得看守很头疼。如果给这些要发火的人吃上肉,他们就会安静点。
输入格式
第一行两个整数
P
P
P 和
Q
Q
Q,
Q
Q
Q 表示释放名单上的人数;
第二行
Q
Q
Q 个整数,表示要释放哪些人。
输出格式
仅一行,表示最少要给多少人次送肉吃。
样例输入 #1
20 3
3 6 14
样例输出 #1
35
样例说明 #1
先释放 14 14 14 号监狱中的罪犯,要给 1 1 1 到 13 13 13 号监狱和 15 15 15 到 20 20 20 号监狱中的 19 19 19 人送肉吃;再释放 6 6 6 号监狱中的罪犯,要给 1 1 1 到 5 5 5 号监狱和 7 7 7 到 13 13 13 号监狱中的 12 12 12 人送肉吃;最后释放 3 3 3 号监狱中的罪犯,要给 1 1 1 到 2 2 2 号监狱和 4 4 4 到 5 5 5 号监狱中的 4 4 4 人送肉吃。
数据规模与约定
- 对于 50 % 50\% 50% 的数据, 1 ≤ P ≤ 100 1 \le P \le 100 1≤P≤100, 1 ≤ Q ≤ 5 1 \le Q \le 5 1≤Q≤5;
- 对于 100 % 100\% 100% 的数据, 1 ≤ P ≤ 1 0 3 1 \le P \le 10^3 1≤P≤103, 1 ≤ Q ≤ 100 1 \le Q \le 100 1≤Q≤100, Q ≤ P Q \le P Q≤P。
分析
这道题看似是将区间“割裂”,但是反过来,我们用合并区间的思想做。
区间DP循环模板:长度 l e n len len+起点 i i i +分割点 k k k
设 f [ i ] [ j ] f[i][j] f[i][j]为合并区间 [ a [ i ] , a [ j ] ] [a[i],a[j]] [a[i],a[j]] 的最小代价。
我们把 a [ 0 ] a[0] a[0] 设为0,最后一个设为n+1。
当你选中一段连续区间中的一个人要放掉的时候,先用递归的思想考虑如何计算。对于最大的一个区间先分割一次,出现两个小区间,再分割直到有
m
−
1
m-1
m−1 个区间,再统计单位区间的答案。但是递归实际上是递推,所以直接从小区间的值算起,然后合并成大区间,合并的代价与分割时计算的代价是一样的。
统计答案那当然是枚举这个区间内放谁,统计这个人在这个区间内影响的人,其实就是“扩展”到整个区间有多少要吃肉的,这个用
a
a
a 数组减就可以了,然后要再减去自己本身和一个区间头的人
状态转移方程: f [ i ] [ j ] = m i n ( f [ i ] [ j ] , f [ i ] [ k − 1 ] + f [ k + 1 ] [ j ] + a [ j + 1 ] − a [ i − 1 ] − 2 ) ; f[i][j]=min(f[i][j],f[i][k-1]+f[k+1][j]+a[j+1]-a[i-1]-2); f[i][j]=min(f[i][j],f[i][k−1]+f[k+1][j]+a[j+1]−a[i−1]−2);
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,a[110],f[1010][1010],last;
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
a[0]=0,a[m+1]=n+1;
for(int len=1;len<=m;len++)
{
for(int i=1;i<=m-len+1;i++)
{
int j=i+len-1;
f[i][j]=0x3f3f3f3f;//只能对于这个初始化,其他都是0,不然没法更新
for(int k=i;k<=j;k++)
{
f[i][j]=min(f[i][j],f[i][k-1]+f[k+1][j]+a[j+1]-a[i-1]-2);
/*跳过k点,统计区间答案,递归思想的话就是变成子问题,但是这个地方
是由小区间合并成大区间,-2是因为有个是本身,有个不存在*/
}
}
}
cout<<f[1][m];
return 0;
}