动态规划 4.7 有向线段k值问题

Posted 天地一扁舟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划 4.7 有向线段k值问题相关的知识,希望对你有一定的参考价值。

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <string>


/*
有向线段k值问题:
给定一条有向直线 L 以及 L 上的 n+1 个点 x0 < x1 < x2 < …< xn 。有向直线 L 上的每个点 xi 都有一个权 w(xi);每条有向边(xi ,xi-1) ,
也都有一个非负边长 d(xi ,xi-1)。有向直线 L 上的每个点xi 可以看作客户,其服务需求量为w(xi) 。
每条边(xi ,xi-1) 的边长 d(xi ,xi-1) 可以看作运输费用。如果在点 xi 处未设置服务机构,
则将点xi 处的服务需求沿有向边转移到点 xj 处服务机构需付出的服务转移费用为w(xi)*d(xi ,xj) 。
已知在点 x0 处设置了服务机构,求要在直线 L 上增设 k 处服务机构,使得整体服务转移费用最小的费用。

其他变量说明:
d(i,j):节点i到节点j之间的距离
m(i,j):节点j的需求转移到节点i的费用
m(i,j)=w(j) * d(i,j)
状态数组:cost[i][j]:表示在第i个节点之后再增设j个服务机构的费用最小值(第i个节点已经被设置为服务机构)
目标状态:cost[0][k]
边界状态:
cost[i][j]=0,i+j>=n时,表示在第i个节点之后增设的服务机构数量大于剩下所能提供服务结构的节点数,这是无效状态
cost[i][j]=m(t,i)的累加和,t从i+1到n ,j=0时,表示第i个节点为增设的最后一个服务点,则从第i+1到n个节点的转移费用之后
,都是相对于第i个节点而言
cost[i][j]=mincost[t][j-1] + m[k][i]的累加和,k从[i+1,t-1],t从[i+1,n-(j-1)] ,当i+j<n,并且j!=0时
状态转移方程:
			0,i+j>=n
cost[i][j]= cost[i][j]=m(t,i)的累加和,t从i+1到n ,j=0
			mincost[t][j-1] + m[k][i]的累加和,k从[i+1,t-1],t从[i+1,n-(j-1)]

如何将增设的服务点路径求出来?
还需要设置一个路径数组,记录顺序吗?
采用状态标记量
service[0~n]记录选择的服务点:
service[i]:第i个服务站点的前一个站点
0不可能的服务点,1:候选服务点,2:真正的服务点

输入:
4(n,但是实际n+1个端点) 2(增设的服务点数k)
0 1 2 3 4(每个端点的需求量)
输出:
4(最小费用)
2(第一个增设的服务点) 4(第二个增设的服务点)
*/

using namespace std;
const int N = 100;
const int K = 100;
int cost[N][K];
int w[N];
int d[N][N];
int m[N][N];
int service[N];

/*
功能:计算cost[i][j],即在第i个节点之后增设j个节点的最小服务费用
输入参数:i:节点i处已经设置为服务点,j:后续还需要设置的服务点数,n:点的个数
返回值:在第i个节点之后增设j个节点的最小服务费用
其实:这里的dp(i,j)=cost[i][j],递归处理的
*/
int dp(int i,int j,int n)

	//记忆化搜索
	if(cost[i][j] != -1)
	
		return cost[i][j];
	
	//递归基
	if(i + j >= n)
	
		cost[i][j] = 0;
		return cost[i][j];
	
	//递归基
	if(j == 0)
	
		//累加求和
		cost[i][j] = 0;
		for(int t = i + 1 ; t <= n ; t++)
		
			//注意,这里加上的是m[i][t],表示t到i的费用
			cost[i][j] += m[i][t];
		
		return cost[i][j];
	
	//递归步,mincost[t][j-1] + m[k][i]的累加和,k从[i+1,t-1],t从[i+1,n-(j-1)]
	int minCost = INT_MAX;
	int minIndex = -1;
	for(int t = i + 1 ; t <= n-(j-1) ; t++)
	
		int iTransferCost = 0;
		//计算传输费用,注意m[i][j]中i与j较大的数必须放在后面
		for(int k = i + 1 ; k <= t - 1 ; k++ )
		
			iTransferCost += m[i][k];
		
		//这里必须用递归计算cost[t][j-1]
		cost[i][j] = dp(t,j-1,n) + iTransferCost;
		int val = cost[i][j];
		if(cost[i][j] < minCost)
		
			//注意,记录最小费用的下标t
			minCost = cost[i][j];
			minIndex = t;
		
	
	cost[i][j] = minCost;
	//将最小下标t写入到路径中
	//service[minIndex] = i;//设置选取的当前站点minIndex的前面一个选择的站点值为i
	//假设我记录的是i对应的minIndex,那就可以了,即,记录当前节点的后一个节点
	service[i] = minIndex;
	//select = minIndex;
	return cost[i][j];


//计算传输费用,m(i,j)=w(j) * d(i,j),i<j,i属于0到n,j属于i+1到n
void compute(int n)

	//给距离数组赋值
	for(int i = 0 ; i < n ; i++)
	
		for(int j = i + 1 ; j <= n ; j++)
		
			d[i][j] = j - i;
		
	
	//计算传输费用
	for(int i = 0 ; i <= n; i++)
	
		for(int j = i + 1 ; j <= n ; j++)
		
			m[i][j] = w[j] * d[i][j];
		
	



void dfsResult(int curIndex , int n)

	cout << curIndex << "," ;
	if(service[curIndex] != -1)
	
		int iNextIndex = service[curIndex];
		dfsResult(iNextIndex , n);
	


void process()

	int n,k;
	while(cin >> n >> k)
	
		//初始化选取的服务点状态
		memset(service , -1 , sizeof(service));
		//初始化权重数组
		memset(w , -1 , sizeof(w));
		//初始化距离数组,默认值为0
		memset(d, 0 , sizeof(d));
		//获取每个端点的权重
		for(int i = 0 ; i <= n ;i++)
		
			cin >> w[i];
		
		//初始化动态规划数组,设定访问标记量,用于剪枝,-1表示未处理过,memset似乎只能针对-1和0进行初始化
		memset(cost , -1 , sizeof(cost));
		//计算任意两点间传输费用数组
		compute(n);
		string sPath;//记录增设服务点的路径,从最后一个增设的服务点到前面的服务点
		//开始进行动态规划
		int select = -1;
		dp(0,k,n);
		cout << "最小费用:" << cost[0][k] << endl;
		cout << "选取的站点是:";
		dfsResult(0 , n);
	


void test()

	memset(cost , 1 , sizeof(cost));
	int  a= 1;


int main(int argc,char* argv[])

	process();
	//test();
	system("pause");
	return 0;



以上是关于动态规划 4.7 有向线段k值问题的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ2090/2089[Poi2010]Monotonicity 2 动态规划+线段树

013--Floyd算法-动态规划-《算法设计技巧与分析》M.H.A学习笔记

动态规划作业-多段图的最短路径问题

bzoj 1835 base 基站选址 - 动态规划 - 线段树

算法 动态规划

动态求区间K大值(权值线段树)