题目大意:
飞机上的座位有S行,编号从1到s,每行有六个座位,标记为A到F。
有n个乘客陆续登机,第
i
i
i名乘客的座位在第
R
i
R_i
Ri行,
第
i
i
i名乘客的登机难度等于在他登机时坐在
1
,
2
,
.
.
.
,
R
i
−
1
1,2,...,R_{i}-1
1,2,...,Ri−1行的乘客的人数。
现在将飞机座位划分为
k
k
k个区域。每一个区域必须是连续的行。
划分成
k
k
k个区域之后,乘客的登机顺序不会改变,但是每个乘客的登机难度将只统计该乘客所在区域前面乘客的人数。
问如何划分这k个区域,才能让乘客的登机难度总和最少。
求这个最小难度总和。
1
≤
R
i
≤
S
1≤Ri≤S
1≤Ri≤S
n
<
=
1000
,
s
<
=
1000
,
k
<
=
50
,
k
<
=
s
n<=1000,s<=1000, k<=50, k<=s
n<=1000,s<=1000,k<=50,k<=s
分析:
设
n
u
m
i
,
j
num_{i,j}
numi,j表示前
j
j
j行的人对第
i
i
i行的人的登机难度的贡献(
j
<
=
i
j<=i
j<=i)
然后这个可以
O
(
S
2
)
O(S^2)
O(S2)处理
我们再设
s
u
m
i
,
j
sum_{i,j}
sumi,j表示第
i
i
i行到第
j
j
j行的人划为一个区域时的登机难度总和,
可推出:
s
u
m
i
,
j
=
s
u
m
i
,
j
−
1
+
(
n
u
m
j
,
j
−
n
u
m
j
,
i
−
1
)
sum_{i,j}=sum_{i,j-1}+(num_{j,j}-num_{j,i-1})
sumi,j=sumi,j−1+(numj,j−numj,i−1)
求这个也是
O
(
S
2
)
O(S^2)
O(S2)
设
d
p
i
,
j
dp_{i,j}
dpi,j表示前
i
i
i行分成了
j
j
j个区域时人的最小登机难度总和,
则我们枚举一个
k
k
k,使前
k
k
k行分
j
−
1
j-1
j−1个区域,
则可以转移
d
p
i
,
j
=
m
i
n
(
d
p
i
,
j
,
d
p
i
,
k
+
s
u
m
k
+
1
,
i
)
dp_{i,j}=min(dp_{i,j},dp_{i,k}+sum_{k+1,i})
dpi,j=min(dpi,j,dpi,k+sumk+1,i)
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>
#define rep(i, st, ed) for (int i = st; i <= ed; i++)
#define rwp(i, ed, st) for (int i = ed; i >= st; i--)
#define inf 0x7fffffff
#define N 1005
using namespace std;
int sum[N][N], dp[N][55], g[N][N], r[N], n, s, K, x;
int main()
{
scanf("%d %d %d", &n, &s, &K);
rep(i, 1, n)
{
scanf("%d", &r[i]);
rep(j, 1, i - 1)
if (r[i] > r[j]) sum[r[i]][r[j]]++;
}
memset(dp, 127, sizeof(dp));
rep(i, 1, s)
rep(j, 1, i) sum[i][j] += sum[i][j - 1];
rep(i, 1, s)
rep(j, i, s)
g[i][j] = g[i][j - 1] + (sum[j][j] - sum[j][i - 1]);
dp[0][0] = 0;
rep(i, 1, s)
rep(j, 1, K)
rep(k, 0, i - 1)
dp[i][j] = min(dp[i][j], dp[k][j - 1] + g[k + 1][i]);
printf("%d\n", dp[s][K]);
}