中文题,题意不用解释了。
分析:
用dp[i][j] 表示i个物品分成j组的最小代价。(这个很关键)做dp题第一步就是,用一个dp数组表示相应问题的含义。这个做好了,写状态方程就容易一些。
根据题意:
当 i==2*j状态方程为:
dp[i][j]=dp[i-1][j]+(a[i]-a[i-1])^2
说明:a[]中的数据是经过从小到大排序的。
理论:如果a1,a2,a3,a4....恰好组成k对,那么最小差值的平方和就是按照升序排列的从左到右的临近组合为一对是最佳的方案。
当i>2*j状态方程为:
dp[i][j]=min(dp[i-1][j],dp[i-2][j-1]+(a[i]-a[i-1])^2)
解释:当i>2*j这时,如果根据上面理论已经组成了j对,但还有i-2*j个没参与组合。故有两种可能,一种是:直接在剩下i-j种选一个,组成新的配对方案(dp[i-1][j])。
或者在一组组合中,抽取一个数(破坏了一对)与剩下的物品组合(dp[i-2][j-1])。这样选取最小的就行了。
解决上面的,代码就好写了。
参考代码:
#include<cstdio> #include<algorithm> #include<cstring> #define pow(a) (a)*(a) using namespace std; int dp[2001][1001],a[2001]; int n,k; void solve() { memset(dp,0,sizeof(dp)); for(int i=2;i<=n;i++){ int r=i/2; for(int j=1;j<=r;j++){ if(i==2*j) dp[i][j]=dp[i-2][j-1]+pow(a[i]-a[i-1]); else dp[i][j]=min(dp[i-1][j],dp[i-2][j-1]+pow(a[i]-a[i-1])); } } } int main() { while(scanf("%d%d",&n,&k)!=EOF){ for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+n+1); solve(); printf("%d\n",dp[n][k]); } return 0; }
做dp的题目非常灵活啊!一定要多练习啊。