題目鏈接:jzoj 5535
題目
小是機場登機的執行經理。他的工作是優化登機流程。飛機上的座位有行,編號從到,每行有六個座位,標記爲到。
今天 有個乘客陸續登機,第名乘客的座位在第行,則第名乘客的登機難度等於在他登機時坐在行的乘客的人數。
例如,如果有名乘客,他們的座位是,,,,,,,,,,那麼他們的登機困難分別是,,,,,,,,,,則難度總和爲。
爲了降低登機難度,小想要將飛機座位劃分爲個區域。每一個區域必須是連續的行。劃分成個區域之後,乘客的登機順序不會改變,但是每個乘客的登機難度將只統計該乘客所在區域前面乘客的人數。
例如,在上面的例子中,如果我們把該平面分成兩個區域: 行和行 ,然後在第一區域中的乘客的座位爲,,,,;在第二區域中的乘客的座位爲,,,,,這種情況下,登機難度綜合爲。
現在,小不知道該怎麼劃分這個區域,才能讓乘客的登機難度總和最少。
輸入
輸入文件第一行包含三個整數,和,下一行包含個整數,輸入保證每一行座位由最多有名乘客。
輸出
輸出文件包含一個整數,表示登機可能的最小登機難度。
樣例輸入
10 12 2
6 4 2 5 2 3 1 11 8 5
樣例輸出
6
數據範圍
的數據,,
的數據,, , , ,
思路
這道題是一道。
我們先通過前綴和求出第排人給第排人造成的登機難度和。(用表示)
接着,通過得出第排到第排爲一個區間時,這個區間的登機難度和。如果用表示,則動態轉移方程則爲:
最後,我們就可以再用求出答案了。設爲對於前排,把它劃分成個區間所最後需要的最小登機難度,那麼這個動態轉移方程就是:
那麼最後我們只需要輸出就可以了。
代碼
#include<cstdio>
#include<cstring>
#define min(x, y) (x) < (y) ? (x) : (y)
using namespace std;
int n, s, k, a[1001], b[1001][1001], f[1001][1001], ans[1001][1001];
int main() {
// freopen("board.in", "r", stdin);
// freopen("board.out", "w", stdout);
scanf("%d %d %d", &n, &s, &k);//讀入
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);//讀入
for (int j = 1; j < i; j++)
if (a[j] < a[i])
b[a[j]][a[i]]++;//記錄某兩行之間產生的登機難度和
}
for (int i = 1; i <= s; i++)
for (int j = 1; j <= s; j++)
b[i][j] += b[i - 1][j];//前綴和
for (int i = 1; i <= s; i++)
for (int j = 1; j <= s; j++)
f[i][j] = f[i][j - 1] + b[j][j] - b[i - 1][j];//dp
memset(ans, 0x7f, sizeof(ans));//初始化
ans[0][0] = 0;//初始化
for (int i = 1; i <= s; i++)
for (int j = 1; j <= k; j++)
for (int l = j; l <= i; l++)//枚舉開始新區塊的位置
ans[i][j] = min(ans[i][j], ans[l - 1][j - 1] + f[l][i]);//dp
printf("%d", ans[s][k]);//輸出
// fclose(stdin);
// fclose(stdout);
return 0;
}