标签: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