标签:poj
| Time Limit: 1000MS | Memory Limit: 10000K | |
| Total Submissions: 17168 | Accepted: 9270 |
Description
Input
Output
Sample Input
10 5 1 2 3 6 7 9 11 22 44 50
Sample Output
9题意是给了V个村庄,要在这些村庄中选出P个建立邮局,要求的是建成了P个邮局之后,所有村庄到邮局的距离最短。求最短距离。
冲着邮局的经典DP做的。暴力的话肯定TLE,所以只能往DP上想。
然后就从小了开始想,如果是从a点到b点(中间b-a+1个村庄)建立1个邮局的话,那肯定是要建在中间的村庄上没跑了。1和3的话,要建在2。1和4的话建在2或者3都行因为距离是一样的。
然后这个规律自己做得时候也发现了:
用sum[i][j]表示村庄i到村庄j建立一个邮局的距离。那么推导有sum[i][j]=sum[i][j-1]+value[j]-value[(i+j)/2]
你看,sum[1][4](建在2或者3)=(value[4]-value[3])+(value[3]-value[2])+(value[3]-value[1])
=value[4]+value[3]-value[2]-value[1]
而sum[1][5](建在3)=(value[5]-value[3])+(value[4]-value[3])+(value[3]-value[2])+(value[3]-value[1])
=value[5]+value[4]-value[2]-value[1]
通过这样可以发现这个规律:sum[i][j]=sum[i][j-1]+value[j]-value[(i+j)/2]
所以这样就把建在1到V个村庄的P个邮局这个问题拆开了,知道了某部分建立一个邮局的最优解,之后就是dp推导。
用dp[i][j]表示从1到j个村庄建立第i个邮局时的最优解,则有dp[i][j]=min(dp[i][j],dp[i-1][k-1]+sum[k][j]);
上面的推导公式表示将1到j个村庄建立i个邮局分成两部分,一部分是1到k-1个村庄建立i-1个邮局的最优解,另一部分是k到j建立一个邮局的最优解,不断从1到j枚举k的取值,选出最小的值即是最优解。
做这道题有一个问题当时想不出来是起始值不知道怎么确定,现在总结的话,真是猪头啊光速小子。初始值不就是dp[1][i]=sum[1][i]啊。当时居然完全没思路想不出来。。。
代码:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
#include <cstring>
#pragma warning(disable:4996)
using namespace std;
int V,P;
int value[305];
int sum[305][305];
int dp[35][305];
int main()
{
int i,j,k;
memset(sum,0,sizeof(sum));
for(i=0;i<=30;i++)
{
for(j=0;i<=300;j++)
{
dp[i][j]=10000;
}
}
cin>>V>>P;
value[0]=0;
for(i=1;i<=V;i++)
{
cin>>value[i];
}
sum[1][2]=value[2]-value[1];
for(i=1;i<=V;i++)
{
for(j=i+1;j<=V;j++)
{
sum[i][j]=sum[i][j-1]+value[j]-value[(i+j)/2];
}
}
for(i=1;i<=V;i++)//!!!!起始值
{
dp[1][i]=sum[1][i];
}
for(i=2;i<=P;i++)
{
for(j=i;j<=V;j++)
{
for(k=1;k<=j;k++)
{
dp[i][j]=min(dp[i][j],dp[i-1][k-1]+sum[k][j]);
}
}
}
cout<<dp[P][V]<<endl;
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:poj
原文地址:http://blog.csdn.net/u010885899/article/details/47283677