[IOI2000][DP]邮局问题

来源:互联网 发布:吉他自学软件排行 编辑:程序博客网 时间:2024/04/29 19:15

在被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 <bits/stdc++.h>#define INF 100000000using 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]);}
0 0
原创粉丝点击