搬寝室【背包问题的变式】⭐⭐⭐⭐⭐

问题描述

在这里插入图片描述

知识点

动态规划

实现

码前反思

我不会这道题,我看答案的。。。

  1. 需要知道的是,我们得对排好序的重量数组进行操作,这个是有理论依据的,这个理论直接影响到后面的状态和状态转移!
    在这里插入图片描述在这里插入图片描述
    综上所述,在选定的最优方案里面,每对物品都是重量相邻的一对物品。 这个看起来很简单,但是我感觉好难证明呀。
  2. 接下来就是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]); 
	}
}

码后反思

  1. 压入错题本,以后看。

二刷代码

那个为什么一定要相邻,我还是没有记住,所以是看的题解,但是之后的过程,我采用了一种动态规划的方式进行求解,非常的好理解~使用到的思想类似于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;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值