题目描述:
排成一条直线的公路上有N个加油站,为了方便加油站的员工吃饭,需要在这N个加油站点位置选K个开设快餐店。那么问题来了,怎样选取能使得每个加油站都可以就近吃饭。所谓的就近吃饭,就是每个加油站的人员会去最近的快餐店吃饭,需要走两个加油站之间的距离。我们的目标是让每个加油站到最近快餐店的距离之和最小(具体可以看样例解释)。
输入:
输入N, K
然后接下来N行,每行输入一个整数ai,表示加油站的位置(这个位置的值是递增的)
输出:
输出最小的距离之和,格式见样例输出。
样例输入:
6 3
5
6
12
19
20
27
样例输出:
Total distance sum = 8
分段时,每段的数量有限制的题型,使用分段DP的方法。可以将问题划分子问题为区间[1,i]划分成j段,最少需要多少代价,那么我们可以用数组dp[i][j]表示前i个快餐店分成j段的最小代价,转移方程为:
dp[i][len]=min(dp[i][len],dp[j][len-1]+cost[j+1][i]);
在这之前,我们需要预处理出[i,j]这一段需要的代价,用cost[i][j]表示。这段区间,快餐店应该设在中位数所在的站点,会使得区间[i,j]一段的代价最小。
综上,我们就可以用分段DP的方式了(哈哈哈哈哈哈哈哈哈哈哈哈哈哈啊哈哈):
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int n,m,a[205],dp[205][35],cost[205][205]; int zxy() { for(int i=1;i<=n;i++) { dp[i][1]=cost[1][i]; for(int j=2;j<=m;j++)dp[i][j]=100000000; } for(int len=2;len<=m;len++) for(int i=len;i<=n;i++) for(int j=len-1;j<=i-1;j++) { dp[i][len]=min(dp[i][len],dp[j][len-1]+cost[j+1][i]); } return dp[n][m]; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) { int mid=(i+j)>>1; cost[i][j]=0; for(int k=i;k<=j;k++) cost[i][j]+=abs(a[mid]-a[k]); } int res=zxy(); printf("Total distance sum = %d ",res); return 0; }