直线K中值问题(动规)

问题描述:

 

  • 在一个按照南北方向划分成规整街区的城市里,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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hnu哈哈

请接受直女的么么哒????

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值