1. 程式人生 > >noi 162 post office dp

noi 162 post office dp

ons style freopen pri class pre 現在 pen mes

大致題意:

有v個村莊,每個村莊有各自的位置,且每個位置互不相同。現在要在村莊上設立P個郵局,使每個村莊到最近的郵局的距離之和最小。

分析:

定義狀態d[i][j]表示前i個村莊,在這i個村莊中設立j個郵局的最小距離。s[i][j]表示村莊i至村莊j這幾個村莊中設立一個郵局的最小距離。如果設立一個郵局,那麽郵局設立在(a+b)/2這個位置是最優的。所以可以分解成以下子問題:

d[i][j]的最小值為d[k][j-1]的最小值加上s[k+1][i],s[k+1][i]為在k+1至i這幾個村莊中設立一個郵局的最小距離。

d[i][j]=min(d[i][j], d[k][j-1]+s[k+1][i])

邊界條件d[i][1]=s[1][i].

s數組可做如下優化:

s[1][4],把郵局設立在2和設立在3上距離是相同的。x2-x1+x3-x2+x4-x2與x3-x1+x3-x2+x4-x3相等。s[1][5]是把郵局設立在3上,s[1][5]=s[1][4]+x[5]-x[3]。由此,可得出遞推式:s[i][j]=s[i][j-1]+x[j]-x[(i+j)/2].

#include <iostream>
#include <cstdio>
using namespace std;

const int
INF=1e8; int x[305]; int d[305][35]; int s[305][305]; int main() { //freopen("in.txt","r",stdin); int n,p; while(~scanf("%d%d",&n,&p)) { for(int i=1;i<=n;i++) scanf("%d",&x[i]); for(int i=1;i<=n;i++) for(int j=1;j<i && j<=p;j++) d[i][j]
=INF; for(int i=1;i<=n;i++) { for(int j=i+1;j<=n;j++) s[i][j]=s[i][j-1]+x[j]-x[(i+j)/2]; d[i][1]=s[1][i]; } for(int i=2;i<=n;i++) for(int j=2;j<=i && j<=p;j++) for(int k=j-1;k<i;k++) d[i][j]=min(d[i][j],d[k][j-1]+s[k+1][i]); printf("%d\n",d[n][p]); } return 0; }

noi 162 post office dp