HDU 1712 ACboy needs your help
分组背包,需要根据题意稍微转化一下。
题中说最多学习课程M
天,可视为背包容量;给的矩阵中的每列代表学习花费j
天(不是第j
天),所以同一行中每列都是互斥的,不能选择超过一个,所以每门课程(每一行)可视为一组,每组内物品最多只能选择一个;另外,这道题中的物品重量就是该物品在矩阵中的列号。
可将分组背包视为普通01背包在第二层循环下再套一个循环,用来枚举该组内的每个物品。
在第三层循环中,可直接将k
的上限设为当前二层循环的j
,因为这里的第三层循环的计数器变量k
本身就代表了当前物品的重量。当然,不管这些,直接在最底层加一个if (k<=j)
也完全可行,只不过前者耗时46ms,后者耗时78ms。
话说又回到背包问题了啊,,大佬写的《背包九讲》真是大开眼界,光一个背包问题就有那么多变形。。顺便知道了分组背包和完全背包还可以有一个优化:对输入数据预处理,先删除一些无用物品。
我想到一个问题,如果这题要求每个组内必须选且仅选一个物品,该怎么求?(加上物品个数作为二维费用,然后对于这一维的非零值都初始化为-INF
?)
看到这个矩阵还想到一个问题,给一个N*N
矩阵,怎么找到N
个数的和最大,且这N
个数两两都不在同一行或同一列?
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
using namespace std; // 分组背包 基础
const int MAX = 101;
int N, M;
int p[MAX][MAX];
int dp[101];
void init()
{
memset(dp, 0, sizeof dp);
}
int main()
{
for (; ~scanf("%d%d", &N, &M);)
{
if (N == 0 && M == 0) break;
init();
for (int i = 1; i <= N; i++)
for (int j = 1; j <= M; j++)
scanf("%d", &p[i][j]);
for (int i = 1; i <= N; i++) // 每组,一门课程(一行)是一组
{
for (int j = M; j >= 1; j--) // 背包容量,最多M天,1是当前组内最小的物品重量(每个组都一样是1)
{
for (int k = 1; k <= j; k++) // 每组内,k就是当前选择的物品的重量,保证k小于等于当前背包可用容量j
{
dp[j] = max(dp[j], dp[j - k] + p[i][k]);
}
}
}
printf("%d\n", dp[M]);
}
return 0;
}