问题描述
知识点
动态规划
实现
码前反思
我不会这道题,我看答案的。。。
- 需要知道的是,我们得对排好序的重量数组进行操作,这个是有理论依据的,这个理论直接影响到后面的状态和状态转移!
综上所述,在选定的最优方案里面,每对物品都是重量相邻的一对物品。 这个看起来很简单,但是我感觉好难证明呀。 - 接下来就是
dp
数组的设计了:
代码实现
//果然DP的状态是最难想的
#include "bits/stdc++.h"
using namespace std;
//最大的物品数
const int maxn = 2010;
//无穷大
const int INF = 0x3fffffff;
//输入的物品数
int n;
//输入的对数
int k;
//物品数组
int arr[maxn];
//dp数组
//dp[i][j]表示在前j个物品里取i对,需要花费的最小疲劳度
int dp[maxn/2][maxn];
int main(){
while(~(scanf("%d%d",&n,&k))){
for(int i=1;i<=n;++i){
scanf("%d",&arr[i]);
}
//进行一个排序
sort(arr+1,arr+n+1);
//书写dp数组的边界
for(int i=1;i<=n;i++){
dp[0][i] = 0; //即在前j间里面取0对是0的疲劳度
}
for(int i=1;i<=k;i++){
for(int j=2*i;j<=n;j++){
//若j>2*i则表明,最后两个物品可以不配对,
//即前j-1件物品足够配成i对,
//所以将dp[i][j]初始化为dp[i][j-1]
if(j > 2*i){
dp[i][j] = dp[i][j-1];
}else{
//若j == 2*i,则说明最后两件物品必须配对
//否则前j件物品配不成i对,所以其状态不能由
//dp[i][j-1]转移而来,dp[i][j]先设置为正无穷
dp[i][j] = INF;
}
if(dp[i][j] > dp[i-1][j-2] + (arr[j-1]-arr[j])*(arr[j-1]-arr[j])){
dp[i][j] = dp[i-1][j-2] + (arr[j-1]-arr[j])*(arr[j-1]-arr[j]);
}
}
}
printf("%d\n",dp[k][n]);
}
}
码后反思
- 压入错题本,以后看。
二刷代码
那个为什么一定要相邻,我还是没有记住,所以是看的题解,但是之后的过程,我采用了一种动态规划的方式进行求解,非常的好理解~使用到的思想类似于0-1背包,表示在前i对中,恰好选择k对能够获得的最小体力!有点小开心 ~
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 2010;
const int inf = 0x3fffffff;
int dp[maxn][maxn/2];
int dif[maxn];
int data[maxn];
int n,k;
int main(){
//freopen("input.txt","r",stdin);
while(scanf("%d %d",&n,&k)!=EOF){
for(int i=0;i<n;i++){
scanf("%d",&data[i]);
}
//排序
sort(data,data+n);
//然后构建一个数组,长度为n-1
for(int i=1;i<n;i++){
dif[i]=pow(data[i]-data[i-1],2);
}
//接下来进行背包问题的求解
fill(dp[0],dp[0]+maxn*(maxn/2),inf);
dp[0][0]=0;
for(int i=0;i<=n-1;i++){
dp[i][0]=0;
}
//对1也要初始化
dp[1][1]=dif[1];
for(int i=2;i<=n-1;i++){
for(int j=1;j<=k;j++){
dp[i][j]=min(dp[i-2][j-1]+dif[i],dp[i-1][j]);//不能够选择i-1,因为共享了一个元素
}
}
//输出结果
printf("%d\n",dp[n-1][k]);
}
return 0;
}