问题描述:
- 在一个按照南北方向划分成规整街区的城市里,n个居民点分布在一条直线上的n个坐标点x<x1<...<xn处。居民们希望在城市中至少选择一一个, 但不超过k个居民点建立服务机构。
- 在每个居民点xi处,服务需求量为wi≥0,在该居民点设置服务机构的费用为ci≥0。假设居民点xi到距其最近的服务机构的距离为di,则居民点xi的服务费用为wi*di。
- 建立k个服务机构的总费用为A+B。A是在k个居民点设置服务机构的费用的总和; B是n个居民点服务费用的总和。
分析:
算法的时空分析
时间复杂度:计算转移费用O(n^2),算法主体O(n*k),因此O(n^2)
空间复杂度:trans[n+1][n+1]存储转移费用,t[n+1][n+1]存储动规子问题的最优解,p[n+1][n+1]存储子问题最优 解的坐标值最大的服务机构的编号。
遇到的问题:当选择城市i作为服务机构,不选择城市i作为服务机构,两种情况下前i个城市的服务总费用相同时,选择第一种情况 ,因为这样能使得后面的城市距离服务机构尽可能地近一些,服务总费用少一些。
#include <iostream>
#include <fstream>
#include <algorithm>
using namespace std;
struct City{
bool operator <(City c){
return x<=c.x;
}
City(){
x=w=c=0;
}
static void init(int n);
float x;
float w;
float c;
};
int t[1001][101],trans[1001][1001],p[1001][1001];//p[i][j]表示前i个城市建立j个服务机构的最优解中,第j个机构的城市编号
City* city;
ifstream in("input.txt");
ofstream out("output.txt");
//计算城市pos到最近的服务机构r寻求服务的费用
float countPlus(int r,int pos){
City c1=city[r],c2=city[pos];
return c2.w*(c2.x-c1.x);
}
//计算所有可能的 i 到 j 服务转移费用
void init(int n){
float cost,mid;
City c1,c2,c3;
//第一家服务机构的建立
for(int j=1;j<=n;j++){
c1=city[j];
cost=c1.c;
for(int i=1;i<j;i++){
c2=city[i];
cost+=c2.w*(c1.x-c2.x);
}
trans[0][j]=cost;
}
//第i个服务机构右侧增加机构j,计算增加的总费用
for(int i=1;i<n;i++)
for(int j=i+1; j<=n;j++){
c1=city[i];
c2=city[j];
cost=c2.c;//建设费用
mid=(c1.x+c2.x)/2;//中点
for(int k=i+1;k<j;k++){
c3=city[k];
if(c3.x>mid){
cost+=c3.w*(c2.x+c1.x-2*c3.x);
}
}
trans[i][j]=cost;
}
}
void solve(int n,int k){
sort(city+1,city+n+1);
init(n);
//计算前i个城市建立j个服务机构的费用
int max;
float f1,f2;
for(int i=1;i<=n;i++){//前i个城市
max=min(k,i);//最多建立的机构数目
for(int j=1;j<=max;j++){//j个机构
f2=t[i-1][j-1]+trans[ p[i-1][j-1] ] [ i ];//第i个城市作为服务机构
if(j==i){//城市数目和机构数目一样多
t[i][j]=f2;
p[i][j]=i;//前i个城市建立k个服务机构最优解中,第j个服务机构是城市i
}
else{
f1=t[i-1][j]+countPlus(p[i-1][j],i);//第i个城市不作为服务机构
t[i][j]=min(f1,f2);//取较小的
if(f2<=f1){//选取城市i,更新p
p[i][j]=i;//前i个城市建立k个服务机构最优解中,第j个服务机构是城市i
}
else {
p[i][j]=p[i-1][j];
}
}
}
}
}
void output(int n,int k){
int min=0x7fffffff,num=0;
for(int i=0;i<=n;i++){
for(int j=0;j<=k;j++)
cout<<t[i][j]<<' ';
cout<<'\n';
}
for(int i=1;i<=k;i++)
if(t[n][i]<min){
min=t[n][i];
num=k;
}
cout<<"建立"<<k<<"个服务机构\n";
cout<<"最优值为"<<min<<'\n';
out<<min;
}
int main(){
int n,k;
in>>n>>k;
city=new City[n+1];
City C;
for(int i=1;i<=n;i++){
in>>C.x>>C.w>>C.c;
city[i]=C;
}
solve(n,k);
output(n,k);
in.close();
out.close();
return 0;
}
测试:
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