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

题意:有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;

}

 

你可能感兴趣的:(HDU)