背包问题

来源:互联网 发布:架子结构图软件 编辑:程序博客网 时间:2024/06/05 16:26

开心的金明【(0-1)背包问题】

Description

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为:
v[j1]w[j1]+v[j2]*w[j2]+ …+v[jk]*w[jk]。(其中为乘号)
请你帮助金明设计一个满足要求的购物单。

Input

输入文件的第1行,为两个正整数,用一个空格隔开:N m
(其中N(<30000)表示总钱数,m(<25)为希望购买物品的个数。)
从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有2个非负整数
v p
(其中v表示该物品的价格(v<=10000),p表示该物品的重要度(1~5))

Output

输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)。

#include<stdio.h>#define Max(x,y) x>y?x:y#define N 30005int f[N]={0},v[30],w[30];int main(){    int i,j,n,m,p;    scanf("%d%d",&n,&m);    for(i=1;i<=m;i++)    {        scanf("%d%d",&v[i],&p);        w[i]=v[i]*p;    }    for(i=1;i<=m;i++)        for(j=n;j>=v[i];j--)            f[j]=Max(f[j],f[j-v[i]]+w[i]);    printf("%d",f[n]);    return 0;}

魔兽争霸之最后的反击【01背包问题】

Description

相传人族与兽族对峙了很久,双方均受到了重创,兽族趁人类没有能力发起大规模进攻之时突然袭击,想一次彻底打败人族。人类为了生存,无论老幼伤病,全部参战,兵分两路抗敌。
由于体质不同,我们以血量表示一个人的战斗力,现在给你所有人的血量,请你把人类分成战斗力最接近的两部分。注意,战斗力要最接近,不然,人族会因你而战败呦!

Input

第一行为一个整数n(1<=n<=36),表示人族的总人数。以下的n行每行一个整数,表示一个人的血量mi(即战斗力),其中1<=mi<=400.

Output

只有一行,包含两个数,即人族的每部分兵的血量总和,较小的一个值放在前面,两个数用空格分隔。

sample

320323535 52
#include<cstdio>bool a[8000]= {1,};int mi[40];int main(){    int n,sum=0;    scanf("%d",&n);    for(int i=0; i<n; i++)    {        scanf("%d",&mi[i]);        sum+=mi[i];    }    for(int i=0; i<n; i++)        for(int j=sum/2; j>=mi[i]; j--)        {            a[j]|=a[j-mi[i]];        }    for(int i=sum/2;; i--)        if(a[i])        {            printf("%d %d",i,sum-i);            break;        }    return 0;}

这个只有血量限制,也就是只有价值,花费可以理解为1

【FATE】【二维费用背包】

最近xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务。久而久之xhd开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级。现在的问题是,xhd升掉最后一级还需n的经验值,xhd还留有m的忍耐度,每杀一个怪xhd会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到0或者0以下时,xhd就不会玩这游戏。xhd还说了他最多只杀s只怪。请问他能升掉这最后一级吗?

Input

输入数据有多组,对于每组数据第一行输入n,m,k,s(0 < n,m,k,s < 100)四个正整数。分别表示还需的经验值,保留的忍耐度,怪的种数和最多的杀怪数。接下来输入k行数据。每行数据输入两个正整数a,b(0 < a,b < 20);分别表示杀掉一只这种怪xhd会得到的经验值和会减掉的忍耐度。(每种怪都有无数个)

Output

输出升完这级还能保留的最大忍耐度,如果无法升完这级输出-1。

Sample Input

10 10 1 10
1 1
10 10 1 9
1 1
9 10 2 10
1 1
2 2

Sample Output

0
-1
1

#include<iostream>#include<algorithm>using namespace std;int cost[1005];int n,m,s,k;int main(){    while(cin>>n>>m>>k>>s)    {        int dp[105][105]= {0},v[105],w[105];        for(int i=1; i<=k; i++)            cin>>v[i]>>w[i];        for(int i=1; i<=k; i++)        {            for(int j=1; j<=s; j++)                for(int q=w[i]; q<=m; q++)                    dp[j][q]=max(dp[j][q],dp[j-1][q-w[i]]+v[i]);        }        int ans=-1,flag=0;        for(int i=1; i<=m; i++){            for(int j=1; j<=s; j++)                if(dp[j][i]>=n)                {                    ans=m-i;                    flag=1;                    break;                }            if(flag) break;        }        cout<<ans<<endl;    }    return 0;}

二维费用背包,状态转移方程就是:f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}

【diving】【多重背包,并且背包装满】

Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal share of the marbles. This would be easy if all the marbles had the same value, because then they could just split the collection in half. But unfortunately, some of the marbles are larger, or more beautiful than others. So, Marsha and Bill start by assigning a value, a natural number between one and six, to each marble. Now they want to divide the marbles so that each of them gets the same total value.
Unfortunately, they realize that it might be impossible to divide the marbles in this way (even if the total value of all marbles is even). For example, if there are one marble of value 1, one of value 3 and two of value 4, then they cannot be split into sets of equal value. So, they ask you to write a program that checks whether there is a fair partition of the marbles.

Input

Each line in the input describes one collection of marbles to be divided. The lines consist of six non-negative integers n1, n2, …, n6, where ni is the number of marbles of value i. So, the example from above would be described by the input-line “1 0 1 2 0 0”. The maximum total number of marbles will be 20000.

The last line of the input file will be “0 0 0 0 0 0”; do not process this line.

Output

For each colletcion, output Collection #k:'', where k is the number of the test case, and then eitherCan be divided.” or “Can’t be divided.”.

Output a blank line after each test case.

Sample Input
1 0 1 2 0 01 0 0 0 1 10 0 0 0 0 0Sample OutputCollection #1:Can't be divided.Collection #2:Can be divided.
#include<iostream>#include<cstring>#include<cmath>using namespace std;const int inf=0x7f7f7f7f;const int N=12e4+5;int item[100],dp[N];int c[20];int main(){    int n[7]= {0};    for(int i=0; i<20; i++) c[i]=1<<i;    int Case=1;    while(scanf("%d%d%d%d%d%d",&n[1],&n[2],&n[3],&n[4],&n[5],&n[6])!=EOF)    {        if(!n[1]&&!n[2]&&!n[3]&&!n[4]&&!n[5]&&!n[6]) break;        int flag=0,k=0,sum=0;        for(int i=1; i<=6; i++) sum+=n[i]*i;        if(sum%2) flag=0;        else        {            for(int i=1; i<=6; i++)            {                int x=n[i],b=0;                while(1)                {                    if(x>=c[b])                    {                        item[k++]=c[b]*i;                        x-=c[b];                        b++;                    }                    else                    {                        item[k++]=x*i;                        break;                    }                }            }            //for(int i=0;i<k;i++) cout<<item[i]<<endl;            //puts("");            for(int i=1; i<N; i++) dp[i]=-inf;            dp[0]=0;            for(int i=0; i<k; i++)                for(int j=sum/2; j>=item[i]; j--)                    dp[j]=max(dp[j-item[i]]+item[i],dp[j]);            //for(int i=0;i<k;i++) cout<<dp[i]<<endl;            //puts("");            if(dp[sum/2]>0) flag=1;        }        printf("Collection #%d:\n",Case);        if(flag) printf("Can be divided.\n\n");        else printf("Can't be divided.\n\n");        Case++;    }    return 0;}

物品数量有限叫多重背包,数量无限叫完全背包,我们可以将数量划分,使得多个相同的物品变成一个整体,从而可以优化时间和空间复杂度。
如果要将背包装满,就需要将dp[1]到dp[n]赋值为负无穷,dp[0]赋值为0,意为装了一个价值为0的物品且已经装满

Piggy-Bank 【完全背包,背包装的最少】

Before ACM can do anything, a budget must be prepared and the necessary financial support obtained. The main income for this action comes from Irreversibly Bound Money (IBM). The idea behind is simple. Whenever some ACM member has any small money, he takes all the coins and throws them into a piggy-bank. You know that this process is irreversible, the coins cannot be removed without breaking the pig. After a sufficiently long time, there should be enough cash in the piggy-bank to pay everything that needs to be paid.

But there is a big problem with piggy-banks. It is not possible to determine how much money is inside. So we might break the pig into pieces only to find out that there is not enough money. Clearly, we want to avoid this unpleasant situation. The only possibility is to weigh the piggy-bank and try to guess how many coins are inside. Assume that we are able to determine the weight of the pig exactly and that we know the weights of all coins of a given currency. Then there is some minimum amount of money in the piggy-bank that we can guarantee. Your task is to find out this worst case and determine the minimum amount of cash inside the piggy-bank. We need your help. No more prematurely broken pigs!

Input

The input consists of T test cases. The number of them (T) is given on the first line of the input file. Each test case begins with a line containing two integers E and F. They indicate the weight of an empty pig and of the pig filled with coins. Both weights are given in grams. No pig will weigh more than 10 kg, that means 1 <= E <= F <= 10000. On the second line of each test case, there is an integer number N (1 <= N <= 500) that gives the number of various coins used in the given currency. Following this are exactly N lines, each specifying one coin type. These lines contain two integers each, Pand W (1 <= P <= 50000, 1 <= W <=10000). P is the value of the coin in monetary units, W is it’s weight in grams.

Output

Print exactly one line of output for each test case. The line must contain the sentence “The minimum amount of money in the piggy-bank is X.” where X is the minimum amount of money that can be achieved using coins with the given total weight. If the weight cannot be reached exactly, print a line “This is impossible.”.

Sample Input
310 11021 130 5010 11021 150 301 6210 320 4
Sample Output
The minimum amount of money in the piggy-bank is 60.The minimum amount of money in the piggy-bank is 100.This is impossible.
#include<bits/stdc++.h>using namespace std;const int inf=1e9;int p[505],w[505];int main(){    int t;    scanf("%d",&t);    while(t--){        int e,f,n;        scanf("%d%d",&e,&f);        scanf("%d",&n);        for(int i=1;i<=n;i++)            scanf("%d%d",&p[i],&w[i]);        int dp[10005],minn=inf;        for(int i=1;i<10005;i++) dp[i]=inf;//赋值为之正无穷很重要        dp[0]=0;        for(int i=1;i<=n;i++)            for(int j=w[i];j<=f-e;j++)                dp[j]=min(dp[j],dp[j-w[i]]+p[i]);//每次取最小        if(dp[f-e]>=inf) printf("This is impossible.\n");        else printf("The minimum amount of money in the piggy-bank is %d.\n",dp[f-e]);        //printf("%d\n",dp[f-e]);    }    return 0;}

这个题关键在于将dp数组先赋值为正无穷,然后在循环的时候每次取最小值

0 0
原创粉丝点击