1160Post Office

我的代码超时了,用太多for了

#include "iostream"
#define INF 10000000;
using namespace std;
int min(int a,int b){return a>b?b:a;}

int main(){
  int n,m,num[320],v,i,j,k,sum,g[320][320],l,dp[320][320];
  while(cin>>n>>m){
    for(i=1;i<=n;i++)cin>>num[i];

    for(v=0;v<n;v++){
      for(i=1;i<=n-v;i++){
        j=i+v;
        g[i][j]=INF;
        for(k=i;k<=j;k++){
          sum=0;
          for(l=i;l<=j;l++){
            sum+=(num[k]-num[l]>0?num[k]-num[l]:num[l]-num[k]);
          }
          g[i][j]=min(g[i][j],sum);
        }
      }
    }
    for(i=1;i<=n;i++)dp[i][1]=g[1][i];
    for(i=2;i<=m;i++){
      for(j=i;j<=n;j++){
        dp[j][i]=INF;
        for(k=j-1;k>=i-1;k--){
          dp[j][i]=min(dp[j][i],dp[k][i-1]+g[k+1][j]);
        }
      }
    }
   cout<<dp[n][m]<<endl;
  }
}

看看人家AC的代码吧,其实有些规律的

区间dp[m][n]表示前面n个点设m个邮局,最后一个可以不放。

那么状态方程dp[m][n] = min{dp[m-1][t] + s[t+1][n] } 

s[t][n]表示在t到n站间设立一个邮局所能达到的最小和。

通过观察发现|xi-xr|+|xn+1-i-xr|>=|xi-xn+1-i|. xr为邮局所在位子

那么|xi-xr|+|xi+1-xr| +....|xn-xr| >= |xn-xi|+|xn-1-xi+1|..

当xr=x(i+n)/2 等号成立

最后可以写成 S[a,b]=S[a,b-1]+S[b]-x[(a+b)/2]

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

#define MX 310
int dp[32][MX],sum[MX][MX];
int P[MX];
int N , V;



int main(){
    int i , j , p ,k;
    scanf("%d %d", &N, &V);
    for(i=0 ; i<N ; i++)
        scanf("%d" , &P[i+1] );

    for(i=1 ; i< N ; i++){
        for(j=i+1 ; j<= N ; j++){
            sum[i][j] = sum[i][j-1]+  P[j]  -  P[(i+j) / 2];
            if(i==1)
                dp[1][j] = sum[i][j];
        }
    }

    for(i=2; i<=V ; i++)
    {
        for(j=i+1 ; j<=N ; j++)
        {
            dp[i][j] = 0x7FFFFFFF;
            for(k=i-1 ; k<=j-1; k++)
            {
                dp[i][j] = min( dp[i][j] , dp[i-1][k] + sum[k+1][j] );
            }
        }
    }

    printf("%d\n" , dp[V][N]);
    return 0;
}

 

 

posted @ 2013-08-27 19:42  龙城星  阅读(174)  评论(0编辑  收藏  举报