FZU1005 Fast Food
Time Limit: 1000 mSec Memory Limit : 32768 KB
题目链接
解题思路
第一反应是二分答案+贪心23333 后来还是考虑了动态规划
f[k][l][r]
其中k
代表仓库数量l
和r
分别代表左右区间,所以首先想到的是动规方程
f[k][l][r]=f[j][l][i]+f[k-j][i+1][r]
将[l,r]
分为两个部分取最优解
后来想到其实每个仓库所控制的区间互不相关,所以原动态转移方程
其实等同于f[k][l][r]=f[k-1][l][i]+f[1][i+1][r]
将左半区间的k-1
个仓库和右边仅有1
个仓库的结果相加 将转移方程优化为:f[k][r]=f[k-1][i]+g[i+1][r]
其中g[l][r]
为[l,r]
中放置一个仓库的最小值 根据推导结果可知当仓库取中间点(l+r)/2
时所有点到仓库的距离和最小
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define MAX 0x3f3f3f3f
int f[205][205], g[205][205], a[205];
int main()
{
int n, m, i, j, , r, l, k, ans, t = 0, mid;
int max1;
while (scanf("%d%d", &n, &m) != EOF)
{
if (n == 0 && m == 0)break;
++t;
memset(g, 0, sizeof(g));
memset(f, 0, sizeof(f));
for (i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (i = 1; i <= n; i++)
for (j = i; j <= n; j++)
{
mid = (i + j) / 2;
for (k = i; k <= j; k++)
g[i][j]+=abs(a[k]-a[mid]);
}
for (j = 1; j <= n; j++) f[1][j] = g[1][j];
for (k = 2; k <= m; k++)
for (r = k; r <= n; r++)
{
f[k][r] = MAX;
for (i = 1; i < j; i++)
f[k][r] = min(f[k][r], f[k - 1][i] + g[i + 1][r]);
}
printf("Chain %d\n", t);
printf("Total distance sum = %d\n\n", f[m][n]);
}
return 0;
}
中点仓库最优证明过程
设数组a1,a2,a3...an 假设n点为仓库点 设此时距离总和为Sn,设n点左右剩余数字的个数分别为a,b
设a[n]与a[n-1]距离为kn 当仓库从 n 移到 n-1 时 Sn-1= Sn - a*kn+(b+1)*kn = Sn - (a-b-1)*kn
因为 kn>0且(a-b-1)单调递减 所以当(a-b-1)正好小于等于0 时 Sn 为最小的距离总和此时 中点为距离总和最小值
所以mid= (1+n)/2