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;
}
posted @ 2019-03-31 21:28  CrossingOver  阅读(161)  评论(0编辑  收藏  举报