240
笔下虽有千言,胸中实无一策

刷题 | POJ 1160 Post Office(邮局问题)

Description:

There is a straight highway with villages alongside the highway. The highway is represented as an integer axis, and the position of each village is identified with a single integer coordinate. There are no two villages in the same position. The distance between two positions is the absolute value of the difference of their integer coordinates. 

Post offices will be built in some, but not necessarily all of the villages. A village and the post office in it have the same position. For building the post offices, their positions should be chosen so that the total sum of all distances between each village and its nearest post office is minimum. 

You are to write a program which, given the positions of the villages and the number of post offices, computes the least possible sum of all distances between each village and its nearest post office. 

Input

Your program is to read from standard input. The first line contains two integers: the first is the number of villages V, 1 <= V <= 300, and the second is the number of post offices P, 1 <= P <= 30, P <= V. The second line contains V integers in increasing order. These V integers are the positions of the villages. For each position X it holds that 1 <= X <= 10000.

Output

The first line contains one integer S, which is the sum of all distances between each village and its nearest post office.

Sample Input

10 5
1 2 3 6 7 9 11 22 44 50

Sample Output

9

Source:

http://poj.org/problem?id=1160

Idea:

邮局问题。

对于在1~n的村庄中建立m个邮局,要求所有村庄到邮局的距离最小。

多个邮局难解,可以先看一个邮局。在1~n中,建一个邮局,可以建在中间,一定是保证总距离最小的。如果建m个,那么就可以认为:在1~k村庄之间已经建了m-1个,然后在在k~n村庄之间只要建一个就够了。而建这一个邮局的最短距离是可以得到的。由此,我们可以将问题分解为多个子问题。

对于这样的一个问题,可以设一个dp[i][j],表示在1~i的村庄中建立了j个邮局的最短总距离。

所以我们可以得到状态转移方程: dp[i][j]=max(dp[i][j],dp[k] [j-1]+sum[k+1] [ j ])

其中sum[i][j]是从i到j的村庄建立一个邮局的最小距离。可以得到:sum[ i ][ j ]=sum[i][j-1]+x[j]-x[ (i+j )/2 ] ;

那么就可以得到dp 的边界:dp[i][1]=sum[1][i];

Code:

#include<stdio.h>
#include<string.h>
int min(int a,int b)
{
	if(a<b)return a;
	else return b;
}
int main()
{
	int V,P,i,j,k,l,x[305],dp[305][305],sum[305][305];
	while(scanf("%d%d",&V,&P)!=EOF)
	{
		memset(sum,0,sizeof(sum));
		for(i=0;i<305;i++)
		    for(j=0;j<305;j++)
		        dp[i][j]=10000000;
		for(i=1;i<=V;i++)
		{
		    scanf("%d",&x[i]);
		    sum[i][i]=0;
		}
		for(i=1;i<=V;i++)
		    for(j=i+1;j<=V;j++)
		    {
		        sum[i][j]=sum[i][j-1]+x[j]-x[(i+j)/2];
		    }
		for(i=1;i<=V;i++)
		    dp[i][1]=sum[1][i];
	
	    for(j=1;j<=P;j++)
	    {
		for(i=j+1;i<=V;i++)
		{
			for(k=1;k<=i;k++)
                        {
				dp[i][j]=min(dp[i][j],dp[k][j-1]+sum[k+1][i]);
			}
		}
		printf("%d\n",dp[V][P]);
	}
	return 0;
}            

or

#include <iostream>  
#include <string.h>  
#include <stdio.h>  
  
using namespace std;  
const int INF = ~0U>>1;  
const int N = 305;  
  
int w[N][N];  
int dp[N][35];  
int a[N];  
int n,m;  
  
void Init(int a[],int n)  
{  
    memset(w,0,sizeof(w));  
    for(int i=1;i<=n;i++)  
        for(int j=i+1;j<=n;j++)  
            w[i][j] = w[i][j-1] + a[j] - a[(i+j)>>1];  
}  
  
int Work()  
{  
    for(int i=1;i<=n;i++)  
    {  
        dp[i][i] = 0;  
        dp[i][1] = w[1][i];  
    }  
    for(int j=2;j<=m;j++)  
    {  
        for(int i=j+1;i<=n;i++)  
        {  
            dp[i][j] = INF;  
            for(int k=j-1;k<i;k++)  
                dp[i][j] = min(dp[i][j],dp[k][j-1] + w[k+1][i]);  
        }  
    }  
    return dp[n][m];  
}  
  
int main()  
{  
    while(~scanf("%d%d",&n,&m))  
    {  
        for(int i=1;i<=n;i++)  
            scanf("%d",&a[i]);  
        Init(a,n);  
        printf("%d\n",Work());  
    }  
    return 0;  
}  

or

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

const int maxn = 310 ;
const int INF = 1<<20 ;

int n , m ;
int a[maxn];
int dp[maxn][maxn];    //dp[i][j] 表示的是在前i个村庄建立j个邮局的最短距离
//dp[i][j] = min(dp[i][j] , dp[k][j-1]+sum[k+1][i]) , (i>j) , j-1<=k<i
//初始条件为:dp[i][1] = sum[1][j] , dp[i][i] = 0 ;
int sum[maxn][maxn];   //sum[i][j] 表示的是在第i个村庄和第j个村庄之间建立一个邮局的最短距离
//有递推关系 sum[i][j] = sum[i][j-1] + a[j] - a[(i+j/2)] ;

int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        for(int i = 1 ; i <= n ; i++)
        {
            scanf("%d",&a[i]);
        }
        memset(sum,0,sizeof(sum));
        for(int i = 1 ; i <= n ; i++)
        {
            for(int j = i+1 ; j <= n ; j++)
            {
                sum[i][j] = sum[i][j-1] + a[j] - a[(i+j)/2] ;
                //cout<<sum[i][j]<<endl;
            }
        }
        for(int i = 1 ; i <= n ; i++)
        {
            dp[i][i] = 0 ;
            dp[i][1] = sum[1][i] ;
            //cout<<dp[i][1]<<endl;
        }

        for(int j = 2 ; j <= m ; j++)   //注意将j放在外层
        {
            for(int i = j+1 ; i <= n ; i++)
            {
                dp[i][j] = INF ;
                for(int k = j-1  ; k < i ; k++)
                {
                    dp[i][j] = min(dp[i][j] , dp[k][j-1]+sum[k+1][i]);
                }
               //cout<<dp[i][j]<<endl;
            }
        }
        printf("%d\n",dp[n][m]);
    }
    return 0;
}

Reference:

http://www.voidcn.com/article/p-msjnzevw-mr.html

http://blog.csdn.net/SeasonJoe/article/details/50411574

posted @ 2017-11-29 05:54  CasperWin  阅读(948)  评论(0编辑  收藏  举报