dp之分组背包hdu3033 最少取1次的解法(推荐)

时间:2021-06-28 00:10:16

题意:有n双鞋子,m块钱,k个品牌,(一个品牌可以有多种价值不同的鞋子),接下来n种不同的鞋子,a为所属品牌,b为要花费的钱,c为所能得到的价值。每种价值的鞋子只会买一双,有个人有个伟大的梦想,每个品牌的鞋子至少买一双,问他这个梦想可以实现不?不可以实现输出Impossible,可以实现输出最大价值......

思路:很容易看出来这是个分组背包题,当然这个分组背包有些不同于每组最多取一个的分组背包......但我是觉得,分组背包就这么几种问法吧

1、最常见的、最水的,每组最多取1个.........(一般是隐性的,需要自己分析)

2、每组至少取1个........(就是本题)

3、随意选,可以选,可以不选,可以只选1个,也可以选多个......(暂时还未学,马上会学).....

对于第一种,模板题,只要你可以分析出来,那么可以水过.....

对于第二种,我想说也是模板题,当然是以本题为基础的模板.........

好吧,这道题目,首先,每组至少取一个,就是说必须要取一个,那么数组dp[i][j],代表的含义就是 前i组容量为j的情况下所得到的最大价值为dpi][j];

同样的,我们首先思考它的状态,每组必须要取一个,那么第i组存在的情况下,第i-1组也必须存在,也是回到了前面所做背包所说的那种“一定”、“必须”的状态,那么同样的在动态转移的时候,要判断它的前一个状态合不合法,我个人比较喜欢用0来判断不合法,>0判断合法.......初始化dp[0][0]=1,最后得到的结果减去1......我想说的是,最后的结果不一定会集中在dp[k][m]上,因为这个状态它不一定存在,也就是说,这个状态不一定合法,当然,也没有关系,我们考虑第k组一定要存在,那么扫描下dp[k][i],取最大值就好.....

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
int dp[15][10005],s[105][2],num[15][105];
queue<int>Q[105];
int main()
{
int n,m,k;
while(scanf("%d %d %d",&n,&m,&k)>0)
{ for(int i=0;i<105;i++)
while(!Q[i].empty())
Q[i].pop();
for(int i=1;i<=n;i++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
Q[a].push(i);
s[i][0]=b;
s[i][1]=c;
} for(int i=1;i<=k;i++)
{
int j=1;
while(!Q[i].empty())
{
num[i][j++]=Q[i].front();
//printf("i==%d %d\n",i,Q[i].front());
Q[i].pop();
}
num[i][0]=j;
} //前面都是将数据处理好
memset(dp,0,sizeof(dp)); //初始化dp
dp[0][0]=1;
int flag=1;
for(int i=1;i<=k;i++) //这是分组
{
if(num[i][0]==1) //要是有一组没有数据,那么说明,它不可能满足每组必须取一个这条件.......
{
flag=0;
break;
}
for(int f=1;f<num[i][0];f++) //不同于最多取一个的分组背包,这里是先放每组有的物品,后放容积.......
{
int xx=num[i][f];;
for(int j=m;j>=0;j--) //至于为什么这么放?我是认为,它是一种模板.......
{
if(j-s[xx][0]>=0&&dp[i][j-s[xx][0]]&&dp[i][j-s[xx][0]]+s[xx][1]>dp[i][j]) //这个判断必须放到第一,以免重复
dp[i][j]=dp[i][j-s[xx][0]]+s[xx][1]; if(j-s[xx][0]>=0&&dp[i-1][j-s[xx][0]]&&dp[i-1][j-s[xx][0]]+s[xx][1]>dp[i][j])//这个必须放在上一个判断下面.....
dp[i][j]=dp[i-1][j-s[xx][0]]+s[xx][1]; }
}
}
int maxx=0;
for(int i=0;i<=m;i++)
if(maxx<dp[k][i])
maxx=dp[k][i];
if(maxx==0||flag==0)
printf("Impossible\n");
else
printf("%d\n",maxx-1);//最大值记得减去1
}
return 0;
}