动态规划-PTA-0/1背包

Programming Contest
Bob will participate in a programming contest. There are altogether n problems in the contest. Unlike in PAT (Programming Ability Test), in a programming contest one can not obtain partial scores. For problem i, Bob will need time[i] to solve it and obtains the corresponding score[i], or he may choose not to solve it at all. Bob will be happy when he obtains a total score no less than happy_score. You are supposed to find the minimum time needed for Bob to be happy. The function need_time must return the minimum time, or -1 if it is impossible for Bob to obtain a score no less than happy_score.

Format of function:

int need_time(const int time[], const int score[], int happy_score, int n);

Here n (1≤nMAXN) is the number of problems; happy_score (1≤ happy_scoreMAXS) is the minimum score for Bob to be happy; time[] is the array to store time[i] (1≤time[i]≤100) which is the time to solve problem i; score[] is the array to store score[i] (1≤score[i]≤100) which is the score Bob gets for solving problem i.

Sample program of judge:

#include <stdio.h>

#define MAXN 10
#define MAXS 1000

int need_time(const int time[], const int score[], int happy_score, int n);

int main() {
  int n, i, happy_score;
  int time[MAXN], score[MAXN];
  scanf("%d %d", &n, &happy_score);
  for (i = 0; i < n; ++i)
      scanf("%d", &time[i]);
  for (i = 0; i < n; ++i)
      scanf("%d", &score[i]);
  printf("%d\n", need_time(time, score, happy_score, n));
  return 0;
}

/* Your function will be put here */

Sample Input:

6 121

84 87 78 16 94 38
87 93 50 22 63 28

Sample Output:

125

本题是0/1背包问题的变种,首先介绍0/1背包问题

0/1背包问题背景

给定背包的承重W,一堆物品的重量weights[ ]和价值values[ ],求背包能装入物品的最大总value

0/1背包问题解法

定义一个二维数组 dp[物品总数][承重+1] 存储当前value,其中 dp[i][j] 表示:从前 i 件物品中找出一些放入背包,在总重不超过承重W的情况下,能达到的最大value。
第 i 件物品重量为 weight[i],价值为 value[i],根据第 i 件物品是否添加到背包中,可以分两种情况讨论:
① 第 i 件物品的重量超过背包承重,即 weight[i] > j。此时就算拿出背包中的所有物品也无法放下,所以它不可能被放入背包,因此有:dp[i][j] = dp[i-1][j]
② 第 i 件物品可以添加到背包中(可能拿出一些已有物品)。此时也可以有放入与不放入两种情况,应取value更大的那种。
(2.1)若不放入,有:dp[i][j] = dp[i-1][j]
(2.2)若放入,情况变为从某个状态下加入第i件物品并达到dp[i][j]结果,可知前一状态时的背包承重应为 j-weight[i]。因此有:dp[i][j] = dp[i-1][ j-weight[i] ] + value[i]

综上,递推式为:

   dp[i][j] = max( dp[i-1][j], max( dp[i-1][ j-weight[i] ] + value[i], dp[i-1][j] ) );
即 dp[i][j] = max( dp[i-1][ j-weight[i] ] + value[i], dp[i-1][j] );
dp数组的一维简化:

可以看到不管第 i 件物品有没有加到背包中,新一轮 i 下的dp值都只由 i-1 决定,并且这种决定对于 j 是单方向的(即较大的 j 处的值只和较小或相同的 j 的值有关),因此若 j 从W -> 0 递减,dp的数组可以依次覆盖。

0/1背包问题的代码:
int* dp = (int*)malloc((W+1)*sizeof(int));
for (int i = 0; i <= W; i++) dp[i] = 0;

for (int i = 0; i <= n; i++) { 			// n = 物品数
	for (int j = W; j >= 1; j--) { 		// 逆序遍历
		if (j >= weights[i]) {
			dp[j] = max(dp[j], dp[ j-weights[i] ] + values[i] );
		}
	}
}
return dp[W];

回到原问题

本问题是求在 sum_score >= happy_score 情况下,sum_time 的最小值。
事实上:

  • 做的题的score >= happy_score <=> 未做题的score < sum_score - happy_score
  • 做的题的 (time) min <=> 未做题的 (time) max

所以原题等价于求出一些没做的题,使得,在这些题的总score不超过sum_score - happy_score情况下这些题的总time取得最大值。
所以:

  • 未做题的总score即为物品的weights
  • sum_score - happy_score即为背包的承重
  • 未做题的总time即为物品的总value

这样我们完成了原问题与0/1背包问题的完美双射。

#include <stdlib.h>
#define max(x,y) ((x)>(y)?(x):(y))

int need_time(const int time[], const int score[], int happy_score, int n)
{
	int sum_time = 0;
	for (int i = 0; i < n; i++) sum_time += time[i];
	int sum_score = 0;
	for (int i = 0; i < n; i++) sum_score += score[i];
	
	if (sum_score < happy_score) return -1;

	int n_happy_score = sum_score - happy_score;

	int* dp = (int*)malloc((n_happy_score+1) * sizeof(int));
	for (int i = 0; i <= n_happy_score; i++) dp[i] = 0;

	for (int i = 0; i < n; i++) {
		for (int j = n_happy_score; j >= 0; j--) {
			if(j >= score[i])
				dp[j] = max(dp[j], dp[j - score[i]] + time[i]);
		}
	}

	return sum_time - dp[n_happy_score];
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值