标签:http one 作用 数组 区间dp cst article std 一点
题意如下:在一条水平的公路上建有n个小屋,两个小屋间的距离是它们的横坐标之差的绝对值。保证小屋的横坐标是整数,以及没有两个小屋建立在同一位置。现在需要建立m所加油站(m<=n),加油站只能建立在小屋所在的位置。
现在需要你写个程序,给定了所有小屋的位置和加油站的数目,计算出每个小屋离最近的加油站的距离总和的最小值。
我觉得这道题最重要的一点是要意识到一点,每个加油站是有它的所划分好的区域的,这样就可以进行区间dp了
第一行包括两个整数n和m: 1 <= n <= 300, 1 <= m <= 30, m <= n. 第二行包括n个整数,代表小屋的位置,以升序的形式列出。对于每一个整数x,1 <= X <= 10000.
这道题我开始的想法没想到先预处理,我一开始先设状态为dp[n][m],然后转移设成dp[n][m]=min(dp[n-1][m-1],dp[n-1][m]),发现这个转移根本转移不下,然后就自闭了,这样错误的原因是什么呢?我没有搞懂它的作用域
接下来我引用一段话这是这个人的博客:https://blog.csdn.net/ccdllyy/article/details/78087848
思路:典型的DP问题。
当我们在v个村庄中只建一个邮局,可以推导出,只有邮局位于中间位置,距离和才最小;有一个特殊情况是,当村庄数为偶数,中间位置有两个村庄,经过计算,两个村庄的距离和相等,所以俩位置均可。
可以联想到,N个村庄建P个邮局,相当于每个邮局均有一个作用范围,该邮局位于其作用范围的中间位置,就是要找到一个k,使前k个村庄建P - 1个邮局,最后几个村庄建一个邮局的方案满足题意。
那么,状态转移方程就可以写成:
dp[i][j]:前i个村庄建j个邮局的最小距离和
dis[i][j]:第i个村庄到第j个村庄之间建1个邮局的最小距离和
状态转移方程:dp[i][j] = min(dp[i][j],dp[k][j - 1] + dis[k + 1][j])
还有一点,计算dis[i][j]时,dis[i][j - 1]已经计算出来,而且可以推导出无论j - 1为奇数还是偶数,dis[i][j]均可以写成dis[i][j - 1] + j距离i、j中点的距离。
那么接下来就不难了,但是我又wa了很多发,原因是数组开小了
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #define inf 0X3f3f3f3f 6 using namespace std; 7 int n,m; 8 int num[350]; 9 int dp[350][350]; 10 int dis[350][350]; 11 int main(){ 12 while(scanf("%d%d",&n,&m)!=EOF){ 13 for(int i=1;i<=n;i++) scanf("%d",&num[i]); 14 memset(dis,0,sizeof(dis)); 15 for(int i=1;i<=n-1;i++){ 16 for(int j=i+1;j<=n;j++){ 17 dis[i][j]=dis[i][j-1]+num[j]-num[(i+j)/2]; 18 } 19 } 20 memset(dp, inf, sizeof(dp)); 21 for(int i=1;i<=n;i++) dp[i][1]=dis[1][i]; 22 for(int i=2;i<=m;i++){ 23 for(int j=i;j<=n;j++){ 24 for(int k=i-1;k<=j-1;k++){ 25 dp[j][i]=min(dp[j][i],dp[k][i-1]+dis[k+1][j]); 26 } 27 } 28 } 29 printf("%d\n",dp[n][m]); 30 } 31 return 0; 32 }
标签:http one 作用 数组 区间dp cst article std 一点
原文地址:https://www.cnblogs.com/pandaking/p/9926535.html