您的位置:首页 > 其它

分组背包专题小结

2015-08-28 21:14 363 查看
先给出分组背包的介绍(参考自:http://blog.csdn.net/nywsp/article/details/7737158

问题

有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

算法

这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设f[k][v]表示前k组物品花费费用v能取得的最大权值,则有:

f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于组k}

使用一维数组的伪代码如下:

for 所有的组k

for v=V..0

for 所有的i属于组k

f[v]=max{f[v],f[v-c[i]]+w[i]}

注意这里的三层循环的顺序,甚至在本文的第一个beta版中我自己都写错了。“for v=V..0”这一层循环必须在“for 所有的i属于组k”之外。这样才能保证每一组内的物品最多只有一个会被添加到背包中。

另外,显然可以对每组内的物品应用P02中“一个简单有效的优化”。

小结

分组的背包问题将彼此互斥的若干物品称为一个组,这建立了一个很好的模型。不少背包问题的变形都可以转化为分组的背包问题(例如P07),由分组的背包问题进一步可定义“泛化物品”的概念,十分有利于解题。

在hdu上发现1712这道题跟P06讲解得思路一模一样,一不小心就把它A了,不过一些稍微为复杂的就得好好考虑了,譬如hdu3033

下面给出相关例题
例题1:

Link:http://acm.hdu.edu.cn/showproblem.php?pid=3535


AreYouBusy

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 3456 Accepted Submission(s): 1345



Problem Description

Happy New Term!

As having become a junior, xiaoA recognizes that there is not much time for her to AC problems, because there are some other things for her to do, which makes her nearly mad.

What's more, her boss tells her that for some sets of duties, she must choose at least one job to do, but for some sets of things, she can only choose at most one to do, which is meaningless to the boss. And for others, she can do of her will. We just define
the things that she can choose as "jobs". A job takes time , and gives xiaoA some points of happiness (which means that she is always willing to do the jobs).So can you choose the best sets of them to give her the maximum points of happiness and also to be
a good junior(which means that she should follow the boss's advice)?



Input

There are several test cases, each test case begins with two integers n and T (0<=n,T<=100) , n sets of jobs for you to choose and T minutes for her to do them. Follows are n sets of description, each of which starts with two integers m and s (0<m<=100), there
are m jobs in this set , and the set type is s, (0 stands for the sets that should choose at least 1 job to do, 1 for the sets that should choose at most 1 , and 2 for the one you can choose freely).then m pairs of integers ci,gi follows (0<=ci,gi<=100), means
the ith job cost ci minutes to finish and gi points of happiness can be gained by finishing it. One job can be done only once.



Output

One line for each test case contains the maximum points of happiness we can choose from all jobs .if she can’t finish what her boss want, just output -1 .



Sample Input

3 3
2 1
2 5
3 8
2 0
1 0
2 1
3 2
4 3
2 1
1 1

3 4
2 1
2 5
3 8
2 0
1 1
2 8
3 2
4 4
2 1
1 1

1 1
1 0
2 1

5 3
2 0
1 0
2 1
2 0
2 2
1 1
2 0
3 2
2 1
2 1
1 5
2 8
3 2
3 8
4 9
5 10




Sample Output

5
13
-1
-1




Author

hphp



Source

2010 ACM-ICPC Multi-University
Training Contest(10)——Host by HEU



AC code:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cstdio>
#include<climits>
#include<algorithm>
#define LL long long
using namespace std;
const int INF=0xffffff;
LL f[110][110];//f[i][j],表示第i组,时间为j时的快乐值。
int v[110][110],w[110][110];//v[i][j]表示第i组第j个物品的容量,w[i][j]表示第i组第j个物品的价值  
int n,t,no[110],type[110];//n为物品组数,t为给出的背包的最大容量,no[i]表示第i组物品的总数量,type[i]表示第i组物品所属类型  
int max(int a, int b)
{
    if(a>b) return a;
    return b;
}
void FZGroupPack()//各种类型的分组背包 
{
    for(int i=1;i<=n;i++)//n为物品组数
    {
        if(type[i]==2)// 2 for the one you can choose freely 随意取
        {
            for(int j=1;j<=no[i];j++) //该循环中物品都至少放了一次 
            {
                for(int k=t;k>=v[i][j];k--)
                {
                     if(f[i][k-v[i][j]]!=-1)
                     	f[i][k] = max(f[i][k] , f[i][k-v[i][j]]+w[i][j]);
                    //对该组进行01背包规划 
                     if(f[i-1][k-v[i][j]]!=-1)
                     	f[i][k] = max(f[i][k] , f[i-1][k-v[i][j]]+w[i][j]);
            //判断当前组该物品放在前面k-v容量是满的情况下,最合适的地方(值最大) 
                }
            }
            for(int j=0;j<=t;j++)  f[i][j] = max(f[i][j] , f[i-1][j]);
            //这个物品放或不放。 
        }
        else if(type[i]==0)//0 stands for the sets that should choose at least 1 job to do//最少选一个
        for(int j=1;j<=no[i];j++)
        {
            for(int k=t;k>=v[i][j];k--)
            {
                      if(f[i][k-v[i][j]]!=-1)//前面有必选的没选,不能再选
                      	f[i][k] = max(f[i][k] , f[i][k-v[i][j]]+w[i][j]);
                      if(f[i-1][k-v[i][j]]!=-1)
                      	f[i][k] = max(f[i][k] , f[i-1][k-v[i][j]]+w[i][j]);
            }
        }
        else if(type[i]==1)//1 for the sets that should choose at most 1,最多取一个 
        {// 01背包:最多选一项,即要么不选,一旦选,只能是第一次选。状态转移方程:dp[i][j]=max(dp[i][j],dp[i-1][j-w[x]]+p[x]); 
            for(int k=t;k>=0;k--)
            {
               for(int j=1;j<=no[i];j++)
               		if(f[i-1][k-v[i][j]]!=-1&&k>=v[i][j])
               			f[i][k] = max(f[i][k] , f[i-1][k-v[i][j]]+w[i][j]);
//第一次f[i][t]=f[i-1][k-v[i][1]即前i-1组(加入当前物品后为k容量,即至少有一个该组物品)
//这个判断是判断选择该组中哪一件物品,当前f[i][k]为最大值 
            }
            for(int j=0;j<=t;j++)  f[i][j] = max(f[i][j] , f[i-1][j]);
            //判断选或不选该物品,会取得最大值 
        }
    } 
} 
int main()
{
    while(scanf("%d%d",&n,&t)!=EOF)//n为组数 ,t为给出的背包的最大容量 
    {
         int sum=0;//至少需要的容量 
         for(int i=1;i<=n;i++)//n为组数 
         {
             int min=INF;
             scanf("%d%d",&no[i],&type[i]);//no[i]表示第i组物品的总数量,type[i]表示第i组物品所属类型 
             for(int j=1;j<=no[i];j++)
             {
                 scanf("%d%d",&v[i][j],&w[i][j]);//v[i][j]表示第i组第j个物品的容量,w[i][j]表示第i组第j个物品的价值 
                 if(type[i]==0&&v[i][j]<min)  min = v[i][j];//0 stands for the sets that should choose at least 1 job to do 
             }
             if(type[i]==0) sum+=min;
         }
         if(sum>t)//剪枝 
         {
             printf("-1\n");
             continue;
         }
         memset(f,-1,sizeof(f)); 
		for(int i=0;i<=t;i++) 
			f[0][i]=0;//f[i][j],表示第i组,时间为j时的快乐值。//如果物品不选就为-1,初始化为-1 
         FZGroupPack();
        if(f
[t]<0)puts("-1");
        else printf("%I64d\n",f
[t]);
         //int ans=0;   
        // cout<<"f= "<<f
[t]<<endl;
        // for(int i=t;i>=0;i--) if(f
[i]>ans) ans=f
[i];
        // if(ans>-1) printf("%d\n",ans);
         //else printf("-1\n"); //没有完成任务的值都为负的,做输出调整,输出-1
    }
    return 0;
}


下面给出该题的讲解思路,来自博客:http://www.cnblogs.com/ACMan/archive/2012/08/13/2637022.html

经典混合背包

题目给了很多类别的物品。用 数组dp[i][j],表示第i组,时间为j时的快乐值。每得到一组工作就进行一次DP,所以dp[i]为第i组的结果。



  第一类,至少选一项,即必须要选,那么在开始时,对于这一组的dp的初值,应该全部赋为负无穷,这样才能保证不会出现都不选的情况。

状态转移方程:

dp[i][j]=max(dp[i][j],max(dp[i][j-w[x]]+p[x],dp[i-1][j-w[x]]+p[x]));




dp[i][j]: 是不选择当前工作;

dp[i-1][j-w[x]]+p[x]: 第一次在本组中选物品,由于开始将该组dp赋为了负无穷,所以第一次取时,必须由上一组的结果推知,这样才能保证得到全局最优解;

dp[i][j-w[x]]+p[x]:表示选择当前工作,并且不是第一次取;



  第二类,最多选一项,即要么不选,一旦选,只能是第一次选。

状态转移方程:

dp[i][j]=max(dp[i][j],dp[i-1][j-w[x]]+p[x]);


由于要保证得到全局最优解,所以在该组DP开始以前,应该将上一组的DP结果先复制到这一组的dp[i]数组里,因为当前组的数据是在上一组数据的基础上进行更新的。



第三类,任意选,即不论选不选,选几个都可以。

状态转移方程为:

dp[i][j]=max(dp[i][j],dp[i][j-w[x]]+p[x]);


同样要保证为得到全局最优解,先复制上一组解,数据在当前组更新。



二维数组版:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define INF 1<<29
int n, m, sum, g;
int w[101],p[101];
int dp[101][101];
int main()
{
    int i, j, x;
    while(~scanf("%d%d",&n,&sum))
    {
        memset(dp,0,sizeof(dp));
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&m,&g);
            for(x=1;x<=m;x++)
                scanf("%d%d",&w[x],&p[x]);
            if(g==0)
            {
                for(j=0;j<=sum;j++)//当前组初始化
                    dp[i][j]=-INF;
                for(x=1;x<=m;x++)
                    for(j=sum;j>=w[x];j--)
                        dp[i][j]=max(dp[i][j],max(dp[i][j-w[x]]+p[x],dp[i-1][j-w[x]]+p[x]));
            }
            else if(g==1)
            {
                for(j=0;j<=sum;j++) //当前组初始化
                    dp[i][j]=dp[i-1][j];
                for(x=1;x<=m;x++)
                    for(j=sum;j>=w[x];j--)
                            dp[i][j]=max(dp[i][j],dp[i-1][j-w[x]]+p[x]);
            }
            else if(g==2)
            {
                for(j=0;j<=sum;j++) //当前组初始化
                    dp[i][j]=dp[i-1][j];
                for(x=1;x<=m;x++)
                    for(j=sum;j>=w[x];j--)
                        dp[i][j]=max(dp[i][j],dp[i][j-w[x]]+p[x]);
            }

        }
        dp
[sum]=max(dp
[sum],-1); //没有完成任务的值都为负的,做输出调整,输出-1
        printf("%d\n",dp
[sum]);
    }
    return 0;
}


滚动数组版:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define INF 1<<29
int n, m, sum, g;
int w[101],p[101];
int dp[101],tmp[101];
int main()
{
    int i, j, x;
    while(~scanf("%d%d",&n,&sum))
    {
        memset(dp,0,sizeof(dp));
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&m,&g);
            for(x=1;x<=m;x++)
                scanf("%d%d",&w[x],&p[x]);
            if(g==0)
            {
                for(j=0;j<=sum;j++)
                {
                    tmp[j]=dp[j];
                    dp[j]=-INF;
                }
                for(x=1;x<=m;x++)
                    for(j=sum;j>=w[x];j--)
                        dp[j]=max(dp[j],max(dp[j-w[x]]+p[x],tmp[j-w[x]]+p[x]));
            }
            else if(g==1)
            {
                for(j=0;j<=sum;j++) //当前组初始化
                    tmp[j]=dp[j];
                for(x=1;x<=m;x++)
                    for(j=sum;j>=w[x];j--)
                            dp[j]=max(dp[j],tmp[j-w[x]]+p[x]);
            }
            else if(g==2)
            {
                for(j=0;j<=sum;j++) //当前组初始化
                    tmp[j]=dp[j];
                for(x=1;x<=m;x++)
                    for(j=sum;j>=w[x];j--)
                        dp[j]=max(dp[j],dp[j-w[x]]+p[x]);
            }

        }
        dp[sum]=max(dp[sum],-1); //没有完成任务的值都为负的,做输出调整,输出-1
        printf("%d\n",dp[sum]);
    }
    return 0;
}


例题2:

Link:http://acm.hdu.edu.cn/showproblem.php?pid=1712


ACboy needs your help

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 5287 Accepted Submission(s): 2863



Problem Description

ACboy has N courses this term, and he plans to spend at most M days on study.Of course,the profit he will gain from different course depending on the days he spend on it.How to arrange the M days for the N courses to maximize the profit?



Input

The input consists of multiple data sets. A data set starts with a line containing two positive integers N and M, N is the number of courses, M is the days ACboy has.

Next follow a matrix A[i][j], (1<=i<=N<=100,1<=j<=M<=100).A[i][j] indicates if ACboy spend j days on ith course he will get profit of value A[i][j].

N = 0 and M = 0 ends the input.



Output

For each data set, your program should output a line which contains the number of the max profit ACboy will gain.



Sample Input

2 2
1 2
1 3
2 2
2 1
2 1
2 3
3 2 1
3 2 1
0 0




Sample Output

3
4
6




Source

HDU 2007-Spring Programming Contest



题意:有n门课程,m天时间,对第i门课花费j天得到的价值是a[i][j],即第i行j列的数字,求最大价值。

编程思想:最多取一个的类型的分组背包。a[i][j]对应的第i个物品花费j天能得到的价值为w[i][j]=a[i][j],此时对应的容量为v[i][j]=j,对于该问题,比较特殊的是,需要自己挖掘出里面背包的相关因素信息,第i组物品的no[i]=t;,no[i]表示第i组物品的总数量即每组物品的数量都是t,type[i]=1;,type[i]表示第i组物品所属类型,类型1表示最多取一个
。根据题意,知道只有最多取一个这种类型的分组背包。

AC code:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cstdio>
#include<climits>
#include<algorithm>
#define LL long long
using namespace std;
LL f[110][110];//f[i][j],表示第i组,时间为j时的快乐值。
int v[110][110],w[110][110];//v[i][j]表示第i组第j个物品的容量,w[i][j]表示第i组第j个物品的价值  
int n,t,no[110],type[110];//n为物品组数,t为给出的背包的最大容量,no[i]表示第i组物品的总数量,type[i]表示第i组物品所属类型  
int a[111][111];
int max(int a, int b)
{
    if(a>b) return a;
    return b;
}
void FZGroupPack()//各种类型的分组背包 
{
    for(int i=1;i<=n;i++)//n为物品组数
    {
        if(type[i]==2)// 2 for the one you can choose freely 随意取
        {
            for(int j=1;j<=no[i];j++) //该循环中物品都至少放了一次 
            {
                for(int k=t;k>=v[i][j];k--)
                {
                     if(f[i][k-v[i][j]]!=-1)
                     	f[i][k] = max(f[i][k] , f[i][k-v[i][j]]+w[i][j]);
                    //对该组进行01背包规划 
                     if(f[i-1][k-v[i][j]]!=-1)
                     	f[i][k] = max(f[i][k] , f[i-1][k-v[i][j]]+w[i][j]);
            //判断当前组该物品放在前面k-v容量是满的情况下,最合适的地方(值最大) 
                }
            }
            for(int j=0;j<=t;j++)  f[i][j] = max(f[i][j] , f[i-1][j]);
            //这个物品放或不放。 
        }
        else if(type[i]==0)//0 stands for the sets that should choose at least 1 job to do//最少选一个
        for(int j=1;j<=no[i];j++)
        {
            for(int k=t;k>=v[i][j];k--)
            {
                      if(f[i][k-v[i][j]]!=-1)//前面有必选的没选,不能再选
                      	f[i][k] = max(f[i][k] , f[i][k-v[i][j]]+w[i][j]);
                      if(f[i-1][k-v[i][j]]!=-1)
                      	f[i][k] = max(f[i][k] , f[i-1][k-v[i][j]]+w[i][j]);
            }
        }
        else if(type[i]==1)//1 for the sets that should choose at most 1,最多取一个 
        {// 01背包:最多选一项,即要么不选,一旦选,只能是第一次选。状态转移方程:dp[i][j]=max(dp[i][j],dp[i-1][j-w[x]]+p[x]); 
            for(int k=t;k>=0;k--)
            {
               for(int j=1;j<=no[i];j++)
               		if(f[i-1][k-v[i][j]]!=-1&&k>=v[i][j])
               			f[i][k] = max(f[i][k] , f[i-1][k-v[i][j]]+w[i][j]);
//第一次f[i][t]=f[i-1][k-v[i][1]即前i-1组(加入当前物品后为k容量,即至少有一个该组物品)
//这个判断是判断选择该组中哪一件物品,当前f[i][k]为最大值 
            }
            for(int j=0;j<=t;j++)  f[i][j] = max(f[i][j] , f[i-1][j]);
            //判断选或不选该物品,会取得最大值 
        }
    } 
} 
int main()
{
	//freopen("D:\in.txt","r",stdin);
    while(scanf("%d%d",&n,&t)!=EOF)//n为组数 ,t为给出的背包的最大容量 
    {
    	if(n==0&&t==0) break;
         int sum=0;//至少需要的容量 
         for(int i=1;i<=n;i++)
         {
         	for(int j=1;j<=t;j++)
         	{
         		scanf("%d",&a[i][j]);
         		w[i][j]=a[i][j];
				v[i][j]=j;
			}
		 }
         for(int i=1;i<=n;i++)//n为组数 
         {
            // int min=9999999;
             //scanf("%d%d",&no[i],&type[i]);//no[i]表示第i组物品的总数量,type[i]表示第i组物品所属类型 
             no[i]=t;//no[i]表示第i组物品的总数量
             type[i]=1;//type[i]表示第i组物品所属类型,类型1表示最多取一个  
            /* for(int j=1;j<=no[i];j++)
             {
                 scanf("%d%d",&v[i][j],&w[i][j]);//v[i][j]表示第i组第j个物品的容量,w[i][j]表示第i组第j个物品的价值 
                 if(type[i]==0&&v[i][j]<min)  min = v[i][j];//0 stands for the sets that should choose at least 1 job to do 
             }
             if(type[i]==0) sum+=min;*/
         }
         /*if(sum>t)//剪枝 
         {
             printf("-1\n");
             continue;
         }*/
        memset(f,-1,sizeof(f));
		for(int i=0;i<=t;i++) 
		 	f[0][i]=0;//f[i][j],表示第i组,时间为j时的快乐值。//如果物品不选就为-1,初始化为-1 
        FZGroupPack();
        if(f
[t]<0)puts("-1");
        else printf("%I64d\n",f
[t]);
        // int ans=0;   
        // cout<<"f= "<<f
[t]<<endl;
        /* for(int i=t;i>=0;i--) if(f
[i]>ans) ans=f
[i];
         if(ans>-1) printf("%d\n",ans);
         else printf("-1\n");*/ //没有完成任务的值都为负的,做输出调整,输出-1
    }
    return 0;
}


例题3:
Link:http://acm.hdu.edu.cn/showproblem.php?pid=3033


I love sneakers!

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 4610 Accepted Submission(s): 1895



Problem Description

After months of hard working, Iserlohn finally wins awesome amount of scholarship. As a great zealot of sneakers, he decides to spend all his money on them in a sneaker store.



There are several brands of sneakers that Iserlohn wants to collect, such as Air Jordan and Nike Pro. And each brand has released various products. For the reason that Iserlohn is definitely a sneaker-mania, he desires to buy at least one product for each brand.

Although the fixed price of each product has been labeled, Iserlohn sets values for each of them based on his own tendency. With handsome but limited money, he wants to maximize the total value of the shoes he is going to buy. Obviously, as a collector, he
won’t buy the same product twice.

Now, Iserlohn needs you to help him find the best solution of his problem, which means to maximize the total value of the products he can buy.



Input

Input contains multiple test cases. Each test case begins with three integers 1<=N<=100 representing the total number of products, 1 <= M<= 10000 the money Iserlohn gets, and 1<=K<=10 representing the sneaker brands. The following N lines each represents a
product with three positive integers 1<=a<=k, b and c, 0<=b,c<100000, meaning the brand’s number it belongs, the labeled price, and the value of this product. Process to End Of File.



Output

For each test case, print an integer which is the maximum total value of the sneakers that Iserlohn purchases. Print "Impossible" if Iserlohn's demands can’t be satisfied.



Sample Input

5 10000 3
1 4 6
2 5 7
3 4 99
1 55 77
2 44 66




Sample Output

255




Source

2009 Multi-University Training Contest 13 - Host
by HIT



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

AC code:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cstdio>
#include<climits>
#include<algorithm>
#define LL long long
using namespace std;
const int INF=0xffffff;
LL f[11][11000];//f[i][j],表示第i组,时间为j时的快乐值。
int v[110][110],w[110][110];//v[i][j]表示第i组第j个物品的容量,w[i][j]表示第i组第j个物品的价值  
int n,t,no[110],type[110];//n为物品组数,t为给出的背包的最大容量,no[i]表示第i组物品的总数量,type[i]表示第i组物品所属类型  
int minv[110];
int max(int a, int b)
{
    if(a>b) return a;
    return b;
}
void FZGroupPack()//各种类型的分组背包 
{
    for(int i=1;i<=n;i++)//n为物品组数
    {
        if(type[i]==2)// 2 for the one you can choose freely 随意取
        {
            for(int j=1;j<=no[i];j++) //该循环中物品都至少放了一次 
            {
                for(int k=t;k>=v[i][j];k--)
                {
                     if(f[i][k-v[i][j]]!=-1)
                     	f[i][k] = max(f[i][k] , f[i][k-v[i][j]]+w[i][j]);
                    //对该组进行01背包规划 
                     if(f[i-1][k-v[i][j]]!=-1)
                     	f[i][k] = max(f[i][k] , f[i-1][k-v[i][j]]+w[i][j]);
            //判断当前组该物品放在前面k-v容量是满的情况下,最合适的地方(值最大) 
                }
            }
            for(int j=0;j<=t;j++)  f[i][j] = max(f[i][j] , f[i-1][j]);
            //这个物品放或不放。 
        }
        else if(type[i]==0)//0 stands for the sets that should choose at least 1 job to do//最少选一个
        for(int j=1;j<=no[i];j++)
        {
            for(int k=t;k>=v[i][j];k--)
            {
                  if(f[i][k-v[i][j]]!=-1)//前面有必选的没选,不能再选
                  	f[i][k] = max(f[i][k] , f[i][k-v[i][j]]+w[i][j]);
                  if(f[i-1][k-v[i][j]]!=-1)
                  	f[i][k] = max(f[i][k] , f[i-1][k-v[i][j]]+w[i][j]);
            }
        }
        else if(type[i]==1)//1 for the sets that should choose at most 1,最多取一个 
        {// 01背包:最多选一项,即要么不选,一旦选,只能是第一次选。状态转移方程:dp[i][j]=max(dp[i][j],dp[i-1][j-w[x]]+p[x]); 
            for(int k=t;k>=0;k--)
            {
               for(int j=1;j<=no[i];j++)
               		if(f[i-1][k-v[i][j]]!=-1&&k>=v[i][j])
               			f[i][k] = max(f[i][k] , f[i-1][k-v[i][j]]+w[i][j]);
//第一次f[i][t]=f[i-1][k-v[i][1]即前i-1组(加入当前物品后为k容量,即至少有一个该组物品)
//这个判断是判断选择该组中哪一件物品,当前f[i][k]为最大值 
            }
            for(int j=0;j<=t;j++)  f[i][j] = max(f[i][j] , f[i-1][j]);
            //判断选或不选该物品,会取得最大值 
        }
    } 
} 
int main()
{
	//freopen("D:\in.txt","r",stdin);
	int zu,a,b,c;
    while(scanf("%d%d%d",&n,&t,&zu)!=EOF)//n为组数 ,t为给出的背包的最大容量 
    {
         int sum=0;//至少需要的容量 
         memset(no,0,sizeof(no));
         for(int i=1;i<=zu;i++)
         	minv[i]=INF;
         for(int i=1;i<=n;i++)//n为组数 
         {
             //scanf("%d%d",&no[i],&type[i]);//no[i]表示第i组物品的总数量,type[i]表示第i组物品所属类型 
             //no[i]=t;//no[i]表示第i组物品的总数量
             type[i]=0;//type[i]表示第i组物品所属类型,类型1表示最多取一个  
             scanf("%d%d%d",&a,&b,&c);
             no[a]++;
             v[a][no[a]]=b;
             w[a][no[a]]=c;
             minv[a]=min(minv[a],v[a][no[a]]);
            /* for(int j=1;j<=no[i];j++)
             {
                 scanf("%d%d",&v[i][j],&w[i][j]);//v[i][j]表示第i组第j个物品的容量,w[i][j]表示第i组第j个物品的价值 
                 if(type[i]==0&&v[i][j]<min)  min = v[i][j];//0 stands for the sets that should choose at least 1 job to do 
             }*/
             //if(type[i]==0) sum+=min;
         }
         n=zu;//n为组数!!!
         for(int i=1;i<=zu;i++)
         	sum+=minv[i];
         if(sum>t)//剪枝 
         {
             printf("Impossible\n");
             //continue;
         }
         else
         {
         	 memset(f,-1,sizeof(f));
			 for(int i=0;i<=t;i++) 
			 	f[0][i]=0;//f[i][j],表示第i组,时间为j时的快乐值。//如果物品不选就为-1,初始化为-1 
	         FZGroupPack();
	        // cout<<"f= "<<f
[t]<<endl;
	         //for(int i=t;i>=0;i--) if(f
[i]>ans) ans=f
[i];
	         if(f[zu][t]<0)puts("Impossible");
        	 else printf("%I64d\n",f[zu][t]);
			 //if(ans>-1) 
			 //printf("%I64d\n",ans);
	         //else printf("Impossible\n"); //没有完成任务的值都为负的,做输出调整,输出-1
		 }
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: