[IOI2000][DP]邮局问题

在被noi题库中一道叫山区建小学[openjudge7624]的一道题虐无数次后见到了这道IOI的原题,不禁感慨我现在回到2000年兴许能进队呢= =|||
好,首先这道题一眼就知道是一个添加号类型DP。

所以我们自然而然的想到了下面的方程:

f[i][j] = min(f[k][j - 1] + dis[k + 1][i], f[i][j]);

  • 在这里,f[i][j]表示前i个城镇建j个邮局的最优解,k为断点位置

那么dis数组怎么求呢,我们有如下方程:

dis[i][j] = dis[i][j - 1] + a[j] - a[(i + j) / 2];

  • 在这里,dis[i][j]表示城镇i到j建立一个邮局的最优解,a为各个城镇坐标

那么解释一下为什么dis[i][j]可以由dis[i][j-1]求出:
1. 首先由贪心策略得,在一些城镇建立一个邮局只要选取最中间的那一个即为最优解,具体证明。
2. 那么为什么可以由前一个状态推过来,首先我们考虑i到j-1的城镇中我们把邮局安放到了k这个位置,对于一个新加入的城镇j,我们会把位置从k挪到k+1,设 X = a[k+1] - a[k],那么对于[i,k]的村庄,每一个距离都多了X,对于[k+1,j]的村庄,每一个都少了X,所以我们会考虑到底移动不移动。
3. 结合第一条的证明,这个答案只会加上a[j]-a[(i + j) / 2]。
4. 证毕

代码:

#include 
#define INF 100000000
using namespace  std;
inline int read(int & t)
{
    register char ch; t = 0; register bool flag = 0;
    while (!((((ch = getchar()) >= '0') && (ch <= '9')) || (ch == '-')));
    ch == '-' ? flag = 1 : t = ch - '0';
    while (((ch = getchar()) >= '0') && (ch <= '9')) t = t * 10 + ch - '0';
    if (flag) t = -t;
}
inline void write(int t)
{
    if (t < 0) {putchar('-'); t = -t;}
    if (t >= 10) write(t / 10);
    putchar(t % 10 + '0');
}
int n, m;
int dis[320][320];
int f[320][40], a[320];
int main()
{
    read(n); read(m);
    memset(f, 0, sizeof(f));
    memset(dis, 0, sizeof(dis));
    for (int i = 1; i <= n; i ++)
        read(a[i]);
    for (int i = 1; i <= n; i ++)
        for (int j = i + 1; j <= n; j ++)
            dis[i][j] = dis[i][j - 1] + a[j] - a[(i + j) / 2];
    for (int i = 1; i <= n; i ++)
        f[i][1] = dis[1][i];
    for (int j = 2; j <= m; j ++)
        for (int i = 1; i <= n; i ++)
        {
            f[i][j] = INF;
            for (int k = 1; k < i; k ++)
                f[i][j] = min(f[k][j - 1] + dis[k + 1][i], f[i][j]);
        }
    write(f[n][m]);
}

你可能感兴趣的:(OI)