题意:
给在一条直线上的n个汉堡店,以及每个汉堡店在x轴上的坐标。
求建m个供应产,使得每个汉堡店到m个供应产的距离和最小。
解析:
经典的邮局dp问题。
首先需要知道到达几个村庄的最小距离和是这几个村庄的坐标中点。
先用一个遍历找出所有i j之间的所有最小距离,然后再dp。
状态转移方程:
dp[ i ] [ j ] = min ( dp[ i ] [ j ] , dp[ i - 1 ] [ k ] + dis[ k + 1 ] [ j ] )
dp[ i ] [ j ] 表示在j个村庄内,建第i个邮局的最小距离。
dp[ i - 1 ] [ k ] + dis[ k + 1 ] [ j ] 表示得的是在 k + 1 到 j 范围内再建一个邮局的总花费。
路径记录下的是k的值,即为下一段邮局管辖范围内的开头村庄坐标值,再知道此段范围末尾的村庄坐标值,就可以算出邮局的地点。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#define LL long long
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 200 + 10;
const int maxm = 30 + 10;
int dp[maxm][maxn];
int path[maxn][maxn];
int dis[maxn][maxn];
int x[maxn];
void print_path(int i, int j)
{
if (i <= 0)
return;
int u = path[i][j];
print_path(i - 1, u);
if (u + 1 == j)
printf("Depot %d at restaurant %d serves restaurant %d\n", i, (u + 1 + j) >> 1, u + 1);
else
printf("Depot %d at restaurant %d serves restaurants %d to %d\n", i, (u + 1 + j) >> 1, u + 1, j);
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
int n, m;
int ca = 1;
while (~scanf("%d%d", &n, &m))
{
if (n == 0 && m == 0)
break;
memset(dp, inf, sizeof(dp));
memset(dis, 0, sizeof(dis));
memset(path, 0, sizeof(path));
memset(x, 0, sizeof(x));
for (int i = 1; i <= n; i++)
{
scanf("%d", &x[i]);
}
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
int mi = (i + j) >> 1;
for (int k = i; k <= j; k++)
dis[i][j] += abs(x[k] - x[mi]);
}
}
//
for (int i = 1; i <= n; i++)
{
dp[1][i] = dis[1][i];
}
for (int i = 2; i <= m; i++)//youju
{
for (int j = 1; j <= n; j++)//cunzhuan
{
for (int k = 1; k < j; k++)
{
int t = dp[i - 1][k] + dis[k + 1][j];
if (t < dp[i][j])
{
dp[i][j] = t;
path[i][j] = k;
}
}
}
}
printf("Chain %d\n", ca++);
print_path(m, n);
printf("Total distance sum = %d\n\n", dp[m][n]);
}
return 0;
}