我的代码超时了,用太多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; }