Luogu P1064 Jin Ming's budget plan [reliable group backpack]

Title description
Jin Ming is very happy today. The key to the new house purchased at home is about to be handed over. There is a very spacious room dedicated to Jin Ming in the new house. What makes him even more happy is that his mother said to him yesterday: "What items need to be purchased in your room and how to arrange them, you have the final say, as long as it does not exceed N yuan." Early this morning, Jin Ming started to make a budget. He divided the items he wanted to buy into two categories: main parts and accessories. The accessories belonged to a certain main part. The following table is some examples of main parts and accessories:

Main accessories

computer printers, scanners

bookcase books

desk lamp, stationery

work chair no

If you want to buy an item classified as an accessory, you must first buy the main item to which the accessory belongs. Each main piece can have 0, 1 or 2 attachments. Attachments no longer have attachments of their own. Jin Ming wants to buy a lot of things, which will definitely exceed the N yuan limit set by his mother. Therefore, he specified an importance level for each item, which was divided into 5 grades: expressed by integers 1~5, the 5th grade is the most important. He also checked the price of each item from the Internet (all in multiples of 10 yuan). He wants to maximize the sum of the products of the price and importance of each item under the premise of not exceeding N dollars (which can be equal to N dollars).

Suppose the price of the jth item is v[j], the importance is w[j], a total of k items are selected, and the serial numbers are j1, j2, ..., jk, then the required sum is:

v[j1] w[j1]+v[j2] w[j2]+ …+v[jk] w[jk]. (where is the multiplication sign)

Please help Jin Ming to design a shopping list that meets the requirements.

Input and output format
Input format:
the first line of input, two positive integers, separated by a space:

N m (where N (<32000) is the total amount of money, and m (<60) is the number of items you want to buy.)

From line 2 to line m+1, line j gives the basic data for item number j-1, each line has 3 non-negative integers

vpq (where v represents the price of the item (v<10000), p represents the importance of the item (1~5), q represents whether the item is a main part or an accessory. If q=0, it means that the item is the main part, If q>0, it means that the item is an accessory, and q is the number of the main item)

Output format:
The output is only a positive integer, which is the maximum value (<200000) of the sum of the product of the price and the importance of the item that does not exceed the total amount of money.

Input Output Sample
Input Sample #1:
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
Output Sample #1:
2200
Description
NOIP 2006 Improvement Group Question 2

[Analysis]:
Basic idea
This is a 01 backpack with dependencies (?)

Since items are divided into main parts and accessories, and each main part contains at most two accessories, we might as well enumerate all main parts. Then, for each enumeration, there will be five cases:


Buy nothing, only buy the main part, buy the main
part and the first accessory, buy the main part
and the second accessory , buy the main part
and two accessories
, just calculate the final value of these four cases, and take the maximum value.

#include <iostream>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 32005;
const int N = 105;
#define ll long long

int n, m;
int a, b, c;
int v[N],q[N],p[N], v1[N],q1[N],v2[N],q2[N],dp[maxn];

int main()
{
    while(cin >> n >> m){
        for(int i=1; i<=m; i++){
            cin >> a >> b >> c;
            if(c==0){
               v[i]=a; q[i]=b;
            }
            else{
                if(q1[c]==0){
                    v1[c]=a;q1[c]=b;}
                else{
                    v2[c]=a; q2[c]=b;}
            }
        }
        for(int i=1; i<=m; i++){
            for(int j=n; j>=v[i]; j--){
                if(j-v[i]>=0){
                    dp[j]=max(dp[j], dp[j-v[i]] + v[i]*q[i]);
                }
                if(j-v[i]-v1[i]>=0){
                    dp[j]=max(dp[j], dp[j-v[i]-v1[i]] + v[i]*q[i] + v1[i]*q1[i]);
                }
                if(j-v[i]-v2[i]>=0){
                    dp[j]=max(dp[j], dp[j-v[i]-v2[i]] + v[i]*q[i] + v2[i]*q2[i]);
                }
                if(j-v[i]-v1[i]-v2[i]>=0){
                    dp[j]=max(dp[j], dp[j-v[i]-v1[i]-v2[i]] + v[i]*q[i] + v1[i]*q1[i] + v2[i]*q2[i]);
                }
            }
        }
        cout<<dp[n]<<endl;
    }
}
此题是01背包问题的变形。物品的重要度乘以价格是背包问题中的价值,物品的价格是背包问题中的体积。

1、我们可以把如何在众多主件与附件之中选择购买的问题转变为看成购买的5种方案:

(1)什么都不买,(2)只买主件,(3)买主件和附件1,(4)买主件和附件2,(5)买主件和两个附件。

2、有些主件有附件,而有些没有,这为我们思考带来了负担,我们完全可以假设任何主件都有两个附件,也就是说如果题目没有给出某个主件的附件的话,我们就假设这个主件的附件是存在的,且价格和重要度都等于0。这个假设首先不会影响到程序的正确性,也不会增加多少运算时间,且这种假设使得我们想问题和写程序都变得简单多了。

3、题目中的价格都是10的这个条件,可以减少一些时间和空间的开销。

此题和01背包问题有2个主要的区别:

区别一:01背包问题对当前物品考虑的只有买和不买两种情况,而此题需要考虑上面所说的5种不同的购买方案。

区别二:01背包问题是用v[i]来保存第i个物品的价值,而此题需要用v[i]来保存第i个物品和它的两个附件的价值,此时我们需要二维数组来实现,物品体积w同样需要用二维数组来保存。

v[i][0]表示第i个物品的主件价值, v[i][1]表示第i个物品的第一个附件的价值,v[i][2]表示第i个物品的第二个附件的价值 .w[i][0..2]表示同样的物品的体积。

f[i,j]表示给定i个物品和j的空间能够获得的最大价值总合。

则: f[i,j]=max{f[i-1,j],

f[i-1,j-w[i,0]]+v[i,0],

f[i-1,j-w[i,0]-w[i,1]]+v[i,0]+v[i,1],

f[i-1,j-w[i,0]-w[i,2]]+v[i,0]+v[i,2],

f[i-1,j-w[i,0]-w[i,1]-w[i,2]]+v[i,0]+v[i,1]+v[i,2]}
其实,此题还有一个关键点,就是输入数据的处理。
根据题目的意思,q是物品的编号,但是这个编号是在考虑附件时统计的编号,而我们认为附件和主件是一体的,因此附件编号因该和主件一致,所以我们需要对题目给出的编号进行转换。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
    using namespace std;  
    const int maxn = 40000;  
    int f[70][maxn];  
    int value[70][3],imp[70][3];  
    int main()  
    {  
        int n,m;  
        int v,p,q;  
        scanf("%d%d",&n,&m);   
        for(int i=1; i<=m; i++)  
        {  
            scanf("%d%d%d",&v,&p,&q);  
            //为主件   
            if (!q)  
            {  
                value[i][0] = v;  
                imp[i][0] = p;  
            }  
            //为附件   
            else  
            {  
                if (!value[q][1])  
                {  
                    value[q][1] = v;  
                    imp[q][1] = p;  
                }  
                else  
                {  
                    value[q][2] = v;  
                    imp[q][2] = p;  
                }  
            }  
        }          
        memset(f,0,sizeof(f));   
        for(int i=1; i<=m; i++)  
        {  
            for(int j=1; j<=n; j++)  
            {  
                if (j-value[i][0]>=0)  
                {  
                    //仅主件   
                    f[i][j] = max(f[i-1][j],f[i-1][j-value[i][0]] + value[i][0]*imp[i][0]);  
                    //这个时候的f[i][j]表示仅有主件的时候的情况,而下面每种加附件的情况,都是在有主件的基础下,所以  
                    //直接和f[i][j]比较   
                    //主件 + 附件1   
                    if (j-value[i][0]-value[i][1]>=0)  
                        f[i][j] = max(f[i][j],f[i-1][j-value[i][0]-value[i][1]] + value[i][0]*imp[i][0] + value[i][1]*imp[i][1]);  
                    //主件 + 附件2   
                    if (j-value[i][0]-value[i][2]>=0)  
                        f[i][j] = max(f[i][j],f[i-1][j-value[i][0]-value[i][2]] + value[i][0]*imp[i][0] + value[i][2]*imp[i][2]);  
                    //主件 + 所有附件  
                    if (j-value[i][0]-value[i][1]-value[i][2]>=0)  
                        f[i][j] = max(f[i][j],f[i-1][j-value[i][0]-value[i][1]-value[i][2]] + value[i][0]*imp[i][0] + value[i][1]*imp[i][1] + value[i][2]*imp[i][2]);  
                }  
                else  
                    f[i][j] = f[i-1][j];  
            }  
        }  
        printf("%d\n",f[m][n]);  
        return 0;  
    }   
P.S:一开始有个assembler messages的错误,纠结的半天,原来是一开始弄的f[maxn][maxn]数组太大爆炸了,

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325672723&siteId=291194637