您的位置:首页 > 其它

HDU3496 Watch the moive (二维01背包)

2017-07-23 16:29 344 查看
题目大意

题目分析
题目类型分析

如何构建DP
二维费用背包传统解法

二维费用背包降维

AC代码
初始化方式v代表花费的件数u代表花费的时间

初始化方式v代表花费的时间u代表花费的件数

原题链接

题目大意

多多有N部最喜欢的movie,每一部需要话费时间t[i],给她带来的幸福指数为v[i]。

两个限制条件:1,看movie的总时间<=L; 2,只能看M部,M<=N.

求多多能获得的最大幸福指数。

题目分析

题目类型分析

这是一道典型的二维费用背包问题。物品是电影,看或不看,0-1选择。

物品的费用之一:时间,选择电影i,就要花费相应的时间t[i];

物品的费用之二:件数,选择电影i,总的件数就要减1。

因为是0-1背包,所以我们需要逆序遍历二维费用:花费的时间,花费的件数。

如何构建DP

二维费用背包传统解法

传统的二维费用是采用一个三维表,设f[i][v][u]表示前i件物品付出两种代价分别为v和u时可获得的最大价值。状态转移方程就是:

f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}

二维费用背包降维

但是,我们可以把上述的i维(描述物品的维数抽掉,仅保留二维费用v,u),达到降维目的。

针对本题,我们有两种选择:

v代表花费的时间,u代表花费的件数

v代表花费的件数,u代表花费的时间

这两者没用实质上的区别,区别的地方在于初始化dp时的方式不同。

回顾题目,我们发现:店家只卖M张碟。也就是说,在件数这个维度上,我们必须达到M维,多不行,少也不行,也就是我们常说的“刚好装满背包”。不过,对于时间维度L,最后看完M张碟的时间<=L就可以了。

当我们选择方式1:v代表花费的时间,u代表花费的件数时:

为了让u维(件数维)刚好装满,我们需要初始化dp[][]矩阵的第一列为0,其他列为-INF.

当我们选择方式2:v代表花费的件数,u代表花费的时间:

为了让v维(件数维)刚好装满,我们需要初始化dp[][]矩阵的第一行为0,其他行为-INF.

AC代码

初始化方式:v代表花费的件数,u代表花费的时间

按照0-1背包的特点,我们需要逆序遍历“件数”维和“时间维”,笔者试过4种dp转移方式(按照该初始化方式,共8种转移方式:2维循环嵌套 * v维2种遍历方式(小到大,大到小) * u维2种遍历方式(小到大,大到小)),其中有的不满足逆序遍历的方式也能AC,笔者对此比较疑惑,望知道的朋友告知。

#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <string>
#include <cmath>
#include <cstring>
using namespace std;

const int INF = 0x3F3F3F3F;
const int MAXN = 105;

int dp[105][1005];
int t[105], v[105];

int main() {
int T;
scanf("%d", &T);
while (T--) {
int N, M, L;
scanf("%d%d%d", &N, &M, &L);
for (int i = 1; i <= N; i++) scanf("%d%d", &t[i], &v[i]);

/*for (int i = 0; i <= M; i++) {
for (int j = 0; j <= L; j++) {
if (i == 0) dp[i][j] = 0;
else dp[i][j] = -INF;
}
}*/
memset(dp,-INF, sizeof(dp));
for (int i = 0; i <= L; i++)dp[0][i] = 0;

for (int i = 1; i <= N; i++) {
/*for (int j = L; j >= t[i]; j--) { //OK
for (int k = 1; k <= M; k++) {
dp[k][j] = max(dp[k][j], dp[k-1][j-t[i]]+v[i]);
}
}*/

/*for (int j = L; j >= t[i]; j--) { //OK
for (int k = M; k >= 1; k--) {
dp[k][j] = max(dp[k][j], dp[k - 1][j - t[i]] + v[i]);
}
}*/

for (int k = M; k >= 1; k--) {  //OK
for (int j = L; j >= t[i]; j--) {
dp[k][j] = max(dp[k][j], dp[k - 1][j - t[i]] + v[i]);
}
}

/*for (int k = 1; k <= M; k++) {    // WA
for (int j = L; j >= t[i]; j--) {
dp[k][j] = max(dp[k][j], dp[k - 1][j - t[i]] + v[i]);
}
}*/

}

cout << (dp[M][L] > 0 ? dp[M][L] : 0) << endl;
}
return 0;
}


初始化方式:v代表花费的时间,u代表花费的件数

这种方式下没有测试各种转移方式,直接采用逆序遍历二维费用。有兴趣的可以试试其它转移方式,猜想和第一种初始化方式差不多:有的可以AC。

#include <iostream>
#include <string.h>
using namespace std;
const int INF = 0X3F3F3F3F;

const int MAXN = 105;
int c[MAXN];
int v[MAXN];
int dp[1005][105];

int main() {
int t; scanf("%d", &t);
while(t--) {
int N, M, L;
scanf("%d%d%d", &N, &M, &L);
for(int i = 1; i <= N; ++i) {
scanf("%d%d", &c[i], &v[i]);
}
memset(dp, -INF, sizeof(dp));
for(int i = 0; i <= L; ++i) {
dp[i][0] = 0;
}

for (int i = 1; i
b394
<= N; ++i) {
for(int j = L; j >= c[i]; j--) {
for (int k = M; k >= 1; --k) {
dp[j][k] = max(dp[j][k], dp[j-c[i]][k-1]+v[i]);
}
}
}
if(dp[L][M] > 0) cout << dp[L][M] << endl;
else cout << 0 << endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息