poj 1160 Post Office
动态规划
1.先考虑在1到n的村长里面放一个邮局的最优解,那么就是把邮局放在最中间的那个村庄处最优(若村庄数为偶数则最中间的村庄有两个哪个都可以)
2.当放m个邮局的时候,其他每个邮局相当于有一个控制范围,范围内的村庄离该邮局最近,那么m个邮局会把n个村庄分为m块,我们就是dp每一块,令每一块的值最优,以及加上还没有处理的大块的最优解,则是我们要的答案
dp[i][j]表示从1号到i号村庄放j个邮局的最优解
方程:dp[i][j]=min{ dp[k-1][j-1]+s[k][i] } s[k][i]表示在k号村庄到i号村庄放1个邮局的最优解,也就是放在最中间
记忆化搜索
/* dp[i][j]=min{ dp[k-1][j-1]+s[k][i] } */ #include <cstdio> #include <cstring> #define INF 0x3f3f3f3f #define N 310 #define M 35 #define ABS(a,b) a-b>0?a-b:b-a #define min(a,b) a<b?a:b int dp[N][M],s[N][N],v[N],n,m; int sum(int a , int b) { int ans=0,mid=(a+b)>>1; for(int i=a; i<=b; i++) ans+=ABS(v[mid],v[i]); return ans; } int dfs(int i ,int j) { int mid,c,k; if(j==1) { dp[i][j]=0; mid=(1+i)>>1; for(c=1; c<=i; c++) dp[i][j]+=ABS(v[mid],v[c]); return dp[i][j]; } if(dp[i][j]!=-1) return dp[i][j]; dp[i][j]=INF; for(k=i; k>=j; k--) //当前分块的右边界 { if(!s[k][i]) //还没有被计算过 s[k][i]=sum(k,i); dp[k-1][j-1]=dfs(k-1,j-1); dp[i][j]=min(dp[i][j],dp[k-1][j-1]+s[k][i]); } return dp[i][j]; } int main() { while(scanf("%d%d",&n,&m)!=EOF) { for(int i=1; i<=n; i++) scanf("%d",&v[i]); memset(dp,-1,sizeof(dp)); memset(s,0,sizeof(s)); dfs(n,m); printf("%d\n",dp[n][m]); } return 0; }
递推
/* 递推版本,应该按照j从小往大递推 dp[i][j]=min{ dp[k-1][j-1]+s[k][i] } */ #include <cstdio> #include <cstring> #define INF 0x3f3f3f3f #define N 310 #define M 35 #define ABS(a,b) a-b>0?a-b:b-a #define min(a,b) a<b?a:b int dp[N][M],s[N][N],v[N],n,m; int sum(int a ,int b) { int ans=0,mid=(a+b)>>1; for(int i=a; i<=b; i++) ans+=ABS(v[mid],v[i]); return ans; } int main() { while(scanf("%d%d",&n,&m)!=EOF) { for(int i=1; i<=n; i++) scanf("%d",&v[i]); for(int i=1; i<=n; i++) for(int j=i; j<=n; j++) s[i][j]=sum(i,j); for(int i=1; i<=n; i++) dp[i][1]=s[1][i]; for(int j=2; j<=m; j++) for(int i=j; i<=n; i++) { dp[i][j]=INF; for(int k=i; k>=j; k--) dp[i][j]=min(dp[i][j],dp[k-1][j-1]+s[k][i]); } printf("%d\n",dp[n][m]); } return 0; }