题解——洛谷P4767 [IOI2000]邮局(区间DP)

这题是一道区间DP

思维难度主要集中在如何预处理距离上

生活经验得,邮局放在中间显然最优

所以我们可以递推求出\( w[i][j] \)表示i,j之间放一个邮局得距离

然后设出状态转移方程

设\( dp[i][j] \)表示从1开始到i放j个邮局的最短距离

然后转移为:\( dp[i][j]=min(dp[k][j-1]+w[k+1][j],dp[i][j]),i \le k \le j \)

显然是个\( O(n^{3}) \)的DP

能够得40分

#include <cstdio>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
int w[3010][3010],dp[3010][310],n,p,x[3010];
signed main(){
  scanf("%lld %lld",&n,&p);
  for(int i=1;i<=n;i++)
    scanf("%lld",&x[i]);
  sort(x+1,x+n+1);
  memset(w,0x3f,sizeof(w));
  for(int i=1;i<=n;i++)
    w[i][i]=0;
  for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
      w[i][j]=w[i][j-1]+x[j]-x[(i+j)/2];
  memset(dp,0x3f,sizeof(dp));
  for(int i=1;i<=n;i++)
    dp[i][1]=w[1][i];
  for(int i=1;i<=p;i++)
    dp[i][i]=0;
  for(int i=2;i<=p;i++)
    for(int j=i+1;j<=n;j++)
      for(int k=i-1;k<=j;k++){
        dp[j][i]=min(dp[j][i],dp[k][i-1]+w[k+1][j]);
//5        printf("%d %d %d %d\n",i,j,k,dp[j][i]);
      }
  /*for(int l=1;l<=n;l++)
    for(int i=1;i+l<=n;++)
      printf("w[%d][%d]=%d\n",i,i+l,w[i][i+l]);*/
  printf("%lld\n",dp[n][p]);
  return 0;
}

然后就是优化

我们可以发现一些显然的性质

\( w[i^{'}][j] \le w[i][j^{'}] , i \le i^{'} \le j \le j^{'} \)

\( w[i][j]+w[i^{'}][j^{'}] \le w[i^{'}][j]+w[i][j^{'}] \)

然后就可以用四边形不等式优化DP了!

然后QwQ

复杂度\( O(n^{2}) \)

没了

#include <cstdio>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
int w[3010][3010],dp[3010][310],n,p,x[3010],s[3010][310];
signed main(){
  scanf("%lld %lld",&n,&p);
  for(int i=1;i<=n;i++)
    scanf("%lld",&x[i]);
  sort(x+1,x+n+1);
  memset(w,0x3f,sizeof(w));
  for(int i=1;i<=n;i++)
    w[i][i]=0;
  for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
      w[i][j]=w[i][j-1]+x[j]-x[(i+j)/2];
  memset(dp,0x3f,sizeof(dp));
  for(int i=1;i<=n;i++)
    dp[i][1]=w[1][i],s[i][1]=0;;
  for(int i=1;i<=p;i++)
    dp[i][i]=0;
  for(int i=2;i<=p;i++){
    s[n+1][i]=n;
    for(int j=n;j>=i+1;j--)
      for(int k=s[j][i-1];k<=s[j+1][i];k++)
        if(dp[j][i]>dp[k][i-1]+w[k+1][j]){
          dp[j][i]=dp[k][i-1]+w[k+1][j];
          s[j][i]=k;
//5        printf("%d %d %d %d\n",i,j,k,dp[j][i]);
        }
      }
  /*for(int l=1;l<=n;l++)
    for(int i=1;i+l<=n;++)
      printf("w[%d][%d]=%d\n",i,i+l,w[i][i+l]);*/
  printf("%lld\n",dp[n][p]);
  return 0;
}

 

posted @ 2018-09-04 19:20  dreagonm  阅读(734)  评论(0编辑  收藏  举报