3-26 直线k中值问题
问题描述
在一个按照南北方向划分成规整街区的城市里,n 个居民点分布在一条直线上的 n 个坐标点x1<x2<...<xnx1<x2<...<xn处。居民们希望在城市中至少选择1个,但不超过k个居民点建立服务机构。在每个居民点 xixi 处,服务需求量为 wi>=0wi>=0 ,在该居民点设置服务机构的费用为 ci>=0ci>=0 。 假设居民点xixi到距其最近的服务机构的距离为didi,则居民点xi的服务费用为wi×diwi×di。
建立 k 个服务机构的总费用为 A+B。A 是在 k 个居民点设置服务机构的费用的总和;B 是 n 个居民点服务费用的总和。
对于给定直线L上的n个点x1<x2<...<xnx1<x2<...<xn,编程计算在直线L上最多设置k处服务机构的最小总费用。
数据输入:
第 1 行有 2 个正整数 n 和 k。n 表示直线 L 上有 n 个点 x1<x2<...<xnx1<x2<...<xn; k是服务机构总数的上限。接下来的n行中,每行有3个整数。第i+1行的3个整数 xixi , wiwi , cici ,分别表示相应居民点的位置坐标,服务需求量和在该点设置服务机构的费用。
Java: version 1
import java.util.Scanner;
public class ZhiXianKZhongZhi {
private static int n,m;
private static int[] wt,swt,x,c,ww,dist;
private static int[][] opt1,opt2;
public static void main(String[] args){
Scanner input = new Scanner(System.in);
int w;
while (true){
n = input.nextInt();
m = input.nextInt();
wt = new int[n+1];
swt = new int[n+1];
x = new int[n+1];
c = new int[n+1];
ww = new int[n+1];
dist = new int[n+1];
opt1 = new int[m+1][n+1];
opt2 = new int[m+1][n+1];
wt[0] = 0;
swt[0] = 0;
for(int i=1; i<=n; i++){
x[i] = input.nextInt();
w = input.nextInt();
c[i] = input.nextInt();
ww[i] = w;
wt[i] = wt[i-1] + w;
dist[i] = x[i] - x[1];
swt[i] = swt[i-1] + w * dist[i];
}
comp();
System.out.println(solution());
}
}
private static int solution(){
int tmp = opt1[1][n];
for(int i=2; i<=m; i++)
if(opt1[i][n] < tmp)
tmp = opt1[i][n];
return tmp;
}
private static void comp(){
int i,j,k,tmp;
for(j=1; j<=n; j++)
opt2[1][j] = c[j] + getw2(0,j);
for(j=1; j<=n; j++) {
opt1[1][j] = opt2[1][1] + getw1(1,j);
for(k=2; k<=j; k++){
tmp = opt2[1][k] + getw1(k,j);
if(opt1[1][j] > tmp)
opt1[1][j] = tmp;
}
}
for(i=2; i<=m; i++){
for(j=i; j<=n; j++){
opt2[i][j] = opt1[i-1][i-1] + getw2(i-1,j);
for(k=i; k<j; k++){
tmp = opt1[i-1][k] + getw2(k,j);
if(opt2[i][j] > tmp)
opt2[i][j] = tmp;
}
opt2[i][j] += c[j];
}
for(j=i; j<=n; j++){
opt1[i][j] = opt2[i][i] + getw1(i,j);
for(k=i+1; k<=j; k++){
tmp = opt2[i][k] + getw1(k,j);
if(opt1[i][j] > tmp)
opt1[i][j] = tmp;
}
}
}
}
private static int getw1(int j, int m){
return (swt[m] - swt[j]) - (wt[m] - wt[j]) * dist[j];
}
private static int getw2(int j, int m){
return (wt[m-1] - wt[j]) * dist[m] - (swt[m-1] - swt[j]);
}
}
Java: version 2
import java.util.Scanner;
public class ZhiXianKZhongZhi1 {
private static int n,m;
private static int[] wt,swt,x,c,ww,dist;
private static int[] opt1,opt2;
private static int min;
public static void main(String[] args){
Scanner input = new Scanner(System.in);
int w;
while (true){
n = input.nextInt();
m = input.nextInt();
wt = new int[n+1];
swt = new int[n+1];
x = new int[n+1];
c = new int[n+1];
ww = new int[n+1];
dist = new int[n+1];
opt1 = new int[n+1];
opt2 = new int[n+1];
wt[0] = 0;
swt[0] = 0;
for(int i=1; i<=n; i++){
x[i] = input.nextInt();
w = input.nextInt();
c[i] = input.nextInt();
ww[i] = w;
wt[i] = wt[i-1] + w;
dist[i] = x[i] - x[1];
swt[i] = swt[i-1] + w * dist[i];
}
comp();
System.out.println(min);
}
}
private static void comp(){
int i,j,k,tmp;
for(j=1; j<=n; j++)
opt2[j] = c[j] + getw2(0,j);
for(j=1; j<=n; j++) {
opt1[j] = opt2[1] + getw1(1,j);
for(k=2; k<=j; k++){
tmp = opt2[k] + getw1(k,j);
if(opt1[j] > tmp)
opt1[j] = tmp;
}
}
min = opt1[n];
for(i=2; i<=m; i++){
for(j=i; j<=n; j++){
opt2[j] = opt1[i-1] + getw2(i-1,j);
for(k=i; k<j; k++){
tmp = opt1[k] + getw2(k,j);
if(opt2[j] > tmp)
opt2[j] = tmp;
}
opt2[j] += c[j];
}
for(j=i; j<=n; j++){
opt1[j] = opt2[i] + getw1(i,j);
for(k=i+1; k<=j; k++){
tmp = opt2[k] + getw1(k,j);
if(opt1[j] > tmp)
opt1[j] = tmp;
}
}
if(opt1[n] < min)
min = opt1[n];
}
}
private static int getw1(int j, int m){
return (swt[m] - swt[j]) - (wt[m] - wt[j]) * dist[j];
}
private static int getw2(int j, int m){
return (wt[m-1] - wt[j]) * dist[m] - (swt[m-1] - swt[j]);
}
}
Input & Output
9 3
2 1 2
3 2 1
6 3 3
7 1 1
9 3 2
15 1 6
16 2 1
18 1 2
19 1 1
19
Reference
王晓东《计算机算法设计与分析》(第3版)P99-100

这篇博客讨论了直线k中值问题,即在一条直线上找到最佳的服务机构位置,以最小化总费用。问题涉及n个居民点和最多k个服务机构,每个点有服务需求量和设置费用。博主提供了两种Java实现版本,并给出了输入输出说明。
1342





