dp[i][j]表示前i個,炸j條路,並且最後一個炸在i的後面時,一到i這一段的最小價值。
dp[i][j]=min(dp[i][k]+w[k+1][i]) w[i][j]表示i到j這一段的價值。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn=1e3+9; int a[maxn]; long long dp[maxn][maxn],w[maxn][maxn],sum[maxn]; int s[maxn][maxn]; int main() { int n,m; while(scanf("%d %d",&n,&m),n) { memset(dp,50,sizeof(dp)); memset(w,0,sizeof(w)); sum[0]=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=a[i]+sum[i-1]; } for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) w[i][j]=w[i][j-1]+a[j]*(sum[j-1]-sum[i-1]); dp[0][0]=0; s[0][1]=0; for(int i=1;i<=n;i++) { s[i][min(m,i)+1]=i-1; for(int j=min(m,i);j>=1;j--) for(int k=s[i-1][j];k<=s[i][j+1];k++) if(dp[i][j]>dp[k][j-1]+w[k+1][i]) { dp[i][j]=dp[k][j-1]+w[k+1][i]; s[i][j]=k; } } long long ans=dp[m][m]+w[m+1][n]; for(int i=m;i<=n;i++) ans=min(ans,dp[i][m]+w[i+1][n]); printf("%lld\n",ans); } return 0; }