#DP# ----- OpenJudge山区建小学

没有记性。到DP不得不写博了,三天后又忘的干干净净。DP是啥 :-)

一道久到不能再久的题了。

OpenJudge  7624:山区建小学

总时间限制: 1000ms     内存限制: 65536kB
描述

政府在某山区修建了一条道路,恰好穿越总共m个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往。已知任意两个相邻的村庄之间的距离为di(为正整数),其中,0 < i < m。为了提高山区的文化素质,政府又决定从m个村中选择n个村建小学(设 0 < n < = m < 500 )。请根据给定的m、n以及所有相邻村庄的距离,选择在哪些村庄建小学,才使得所有村到最近小学的距离总和最小,计算最小值。

输入
第1行为m和n,其间用空格间隔
第2行为(m-1) 个整数,依次表示从一端到另一端的相邻村庄的距离,整数之间以空格间隔。

例如
10 3
2 4 6 5 2 4 3 1 3
表示在10个村庄建3所学校。第1个村庄与第2个村庄距离为2,第2个村庄与第3个村庄距离为4,第3个村庄与第4个村庄距离为6,...,第9个村庄到第10个村庄的距离为3。
输出
各村庄到最近学校的距离之和的最小值。
样例输入
10 2
3 1 3 1 1 1 1 1 3
样例输出
18


因为所求为距离和最小,所以发现选择一个点a建立学校后,以a为中心向两边扩散下一个点所走的距离d=d(前一个点)+d(当前);期望重复次数越多路段d越小。然后,就有dalao证明了区间[i,j]中在中点处建学校路段和为最小(建1所学校);脑洞又开到:如果区间内点数为偶数,如何选择中点?手推数据发现其实都是一样的。在此计算的为距离和,所以实则计算的是 d总+=di*重复次数。如图:

考虑DP:从第一个村庄开始扩展,在区间[1,i],每加入一个点i进行决策 。

状态转移方程:f[i][j]=min(f[i][j],f[k][j-1]+w[k+1][i]); 

f[i][j] 表示到第i个点建j所学校的最优;在区间[1.i]间枚举断点,断点前为在区间[1,k]建j-1所学校的最优,断点后为在区间[k+1,i]建1所学校的最优。

 1 #include<cstdio>
 2 #include<cmath>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 
 7 int n,m;
 8 int d[550][550],f[550][550],w[550][550],s[550];
 9 
10 int pre(int a,int b){
11     int x=0;
12     int mid=(a+b)>>1;
13     for(int i=a;i<=b;++i)
14        x+=d[i][mid];
15     return x;   
16 }
17 
18 int main(){
19     scanf("%d%d",&n,&m);
20     for(int i=2;i<=n;++i){//s[i]---第i-1个村庄到第i个村庄间的距离 
21         int x;
22         scanf("%d",&x);
23         s[i]=s[i-1]+x; 
24     }
25     memset(f,0x3f3f3f,sizeof(f));
26     for(int i=1;i<=n;++i){
27        f[i][i]=0;
28        for(int j=1;j<=n;++j){
29            if(i==j)d[i][j]=0;
30            d[i][j]=d[j][i]=abs(s[i]-s[j]);//(i,j)间的距离 
31        }
32     }
33     for(int i=1;i<=n;++i)//(i,j)建1所小学min 
34        for(int j=i+1;j<=n;++j)
35          w[i][j]=pre(i,j);  
36     for(int i=1;i<=n;++i)f[i][1]=w[1][i];
37     for(int i=2;i<=n;++i)// 村庄 
38        for(int j=2;j<=min(i,m);++j)//j---学校  min(枚举的村庄数,学校数) 
39           for(int k=j-1;k<i;++k)//min( f[i][j],建j-1所学校所达范围(1,k)+新建第j所学校范围(k+1,i)) 
40               if(i!=j)f[i][j]=min(f[i][j],f[k][j-1]+w[k+1][i]);
41                  
42     printf("%d",f[n][m]);      
43     return 0;
44 }

 

 



 

posted @ 2016-10-28 12:37  WJ-Ting  阅读(198)  评论(0)    收藏  举报