现在的位置: 首页 > 综合 > 正文

POJ1160 dp

2014年01月11日 ⁄ 综合 ⁄ 共 1330字 ⁄ 字号 评论关闭

求:在村庄内建邮局,要使村庄到邮局的距离和最小。

设有m个村庄,分别为 V1 V2 V3 … Vm, 要建n个邮局,分别为P1 P2 P3 … Pn。

在DP的问题中,经常有从m个物体中选n个物体的情况,本题显然也属于这种情况。一般可以这样考虑:假设已经选了1个,那么就成了在m-1个中选n-1个的问题了。

对于此题,也可以考虑先建一个邮局。建在哪里呢?不妨设,该邮局建在Vk+1..Vm之间的某个村庄里,而且规定Vk+1..Vm之间再不允许建立其他邮局了。因此,剩下的n-1个邮局必然出现在V1..Vk之间的村庄内,这样问题就转换成:在前k个村庄里选n-1个邮局的问题。

设dp( m,n )表示在V1…Vm之间建立n个邮局时的最短距离。

L( i, j )表示在Vi…Vj之间建立一个邮局的最短距离。

状态转换方程:

dp( m,n ) = Min( dp( k,n-1 ) + L( k+1,m ) ),    其中:1 <= k<m

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;

const int V_MAX = 305;
const int P_MAX = 35;
const int INF = 9999999;
//dis[i][j] means smallest sum dis when build a post office in villages  from i to j
int dis[V_MAX][V_MAX];
//dp[i][j] means smallest sum dis when build j post offices in villages  from 1 to i
int dp[V_MAX][P_MAX];
int vnum,pnum;
int vpos[V_MAX];

void solve()
{
	//compute dis
	memset(dis,0,sizeof(dis));
	int i,j;
	for(i=1;i<=vnum;i++)
	{
		for(j=i+1;j<=vnum;j++)
		{
			int mid = (i+j) >> 1;//build a post office at middle village can get smallest sum dis
			for(int k=i;k<=j;k++)
				dis[i][j] += abs(vpos[k]-vpos[mid]);
		}
	}
	for(i=1;i<=vnum;i++)
		dp[i][0] = INF;
	for(i=1;i<=vnum;i++)
	{
		for(j=1;j<=pnum&&j<i;j++)
		{
			int min = INF;
			for(int k=j-1;k<i;k++)
				if(dp[k][j-1] + dis[k+1][i] < min)
					min = dp[k][j-1] + dis[k+1][i];
			dp[i][j]=min;
		}
	}
	
	cout<<dp[vnum][pnum]<<endl;
}
int main()
{
	//freopen("1160.txt","r",stdin);
	cin>>vnum>>pnum;
	for(int i=1;i<=vnum;i++)
		cin>>vpos[i];
	solve();
		
}

此题分析转载自 http://blog.csdn.net/sj13051180/article/details/6669737

抱歉!评论已关闭.