题目大意:一群牛要上太空,给出n种石块,每种石块给出单块高度h_i,使用第i种石头后高度不能超过a_i,数量c_i,要求用这些石块能组成的最大高度
题目链接:点击打开链接
分析:
这个问题为典型的多重部分和,也属于完全背包可行性问题。与裸的多重部分和相比,多了一个条件a_i,所以不能再裸多重部分和的代码了。
根据贪心的思想,对于a_i小的当然是堆在越下面越好,所以不妨先对a_i升序排序,这时当检查到第i种石头时,可以组成的最大高度肯定为a_i,因为a_i为前i种里的最大值。这个时候再裸上一个多重部分和的模板就好了。
状态:dp[i][j]:前i种石头且当前正使用第i种堆出高度j最多剩下多少第i种石头
转移:
①若dp[i-1][j]>=0,即前i-1种可以配成j,所以根本用不到第i种,所以剩余C_i种 dp[i][j]=C_i
②若j<a[i] || dp[i][j-a[i]]<=0,由于dp[i-1][j]<0,所以要想配成j起码得要有第i种,若j<a[i]则第i种用不到,所以前i种仍配不到j,若dp[i][j-a[i]]<=0,则说明配成j-a[i]时第i种已经无剩余或者甚至无法配成j-a[i],更别说配成j了, dp[i][j]=-1
③其他情况,由于a[i]还有剩,所以dp[i][j]相当于在dp[i][j-a[i]]的基础上多使用了一个a[i],此时 dp[i][j]=dp[i][j-a[i]]-1
附上代码:
#include<iostream>
#include<algorithm>
using namespace std;
struct Block{ int h, a, c; }block[405];
int dp[40005]; //dp[i][j]:前i种且当前正使用第i种堆出j时第i种最多剩余的个数
int n, ans = 0;
bool cmp(Block &aa, Block &bb){ return aa.a < bb.a; }
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d%d%d", &block[i].h, &block[i].a, &block[i].c);
sort(block + 1, block + n + 1, cmp);
memset(dp, -1, sizeof dp);
dp[0] = 0;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= block[i].a; j++)
if (dp[j] >= 0) dp[j] = block[i].c;
else if (j < block[i].h || dp[j - block[i].h] <= 0) dp[j] = -1;
else dp[j] = dp[j - block[i].h] - 1;
for (int i = 1; i <= block[n].a; i++)
if (dp[i] >= 0) ans = i;
printf("%d\n", ans);
return 0;
}