P4767 [IOI2000]邮局题解
题意:
题很简单,大意是给你
思路:
首先显然动态规划嘛O(∩_∩)O
数组
【再建一个数组
---------状态转移方程分割线---------
-
f_{i,j}=\min{f_{k,j-1}+min_{k+1,j}}
很显然不用过多解释了吧(假的。
首先,
j 个镖局最少要有j 个村庄(这不是废话吗)。好,那么接下来,i个村庄j个镖局如果再加上
x 个村庄这种方法是被允许的。于是,
k 的范围应该在j-1 到i 之间。
-
min_{i,j}=min_{i,j-1}+location_{j}-location_{(i+j)\div2}
这也不多说了,刚才说过了(详见用【】框起来的部分)
-----------50分代码-----------
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int n , p , d[3005] , f[3005][305] , m[3005][3005];
int main(){
scanf ("%d%d" , &n , &p);
for(int i = 0; i < n; i ++)scanf ("%d" , &d[i]);
for(int i = 0; i < n; i ++)
for(int j = 0; j <= min(i , p - 1); j ++)
f[i][j] = 0x3f3f3f3f;
for(int i = 0; i < n; i ++){
for(int j = i + 1; j < n; j ++)
m[i][j] = m[i][j - 1] + d[j] - d[i + j >> 1];
f[i][0] = m[0][i];
}
for(int i = 1; i < n; i ++)
for(int j = 1; j <= min(i , p - 1); j ++)
for(int k = j - 1; k < i; k ++)
f[i][j] = min(f[i][j] , f[k][j - 1] + m[k + 1][i]);
printf ("%d" , f[n - 1][p - 1]);
return 0;
}
但是这个程序的运算次数为
但我们发现放邮局的地方是具有单调性的,因为如果放的再靠前的话他也不如放在后面好,所以我们可以用一个数组
把这个加上的话,就可以去掉一些重复部分,所以可以过。
-----------AC 代码-----------
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f , N = 3010 , M = 310;
int n , p;
int d[N];
int f[N][M] , g[N][N] , P[N][N];
int i , j , k;
int main(){
scanf ("%d%d" , &n , &p);
for(i = 0; i < n; ++ i)scanf ("%d" , &d[i]);
for(i = 0; i < n; ++ i)
for(j = 0; j <= min(i , p - 1); j ++)
f[i][j] = INF;
for(i = 0; i < n; ++ i){
for(j = i + 1; j < n; ++ j)
g[i][j] = g[i][j - 1] + d[j] - d[(i + j) / 2];
f[i][0] = g[0][i];
}
for(i = 1; i < n; ++ i)
for(j = 1; j <= min(i , p - 1); ++ j) {
for(k = P[i][j - 1]; k < i; ++ k)
if (f[i][j] > f[k][j - 1] + g[k + 1][i]) {
f[i][j] = f[k][j - 1] + g[k + 1][i];
P[i][j] = k;
}
}
printf ("%d" , f[n - 1][p - 1]);
return 0;
}