01背包 动态规划

题目描述:

有一个国家,所有的国民都非常老实憨厚,某天他们在自己的国家发现了n座金矿,并且这n座金矿在地图上排成一条直线,国王知道这个消息后非常高兴,他希望能够把这些金子都挖出来造福国民,首先他把这些金矿按照在地图上的位置从西至东进行编号,依次为1,2…,n-1,n,然后他命令他的手下去对每一座金矿进行勘测,以便知道挖取每一座金矿需要多少人力以及每座金矿能够挖出多少金子,然后动员国民都来挖金子。最后,国王招募到m个人,并且知道在第i个金矿派出people[i]个人就能得到gold[i]的金子,否则则颗粒无收。那么我们要怎么分配人员来使得收益最高呢?

输入输出:

多组输入,每组输入的
第一行输入两个数m(0~10000)和n(0~100),表示人员数量和金矿数量。
第二行至第n+1行,每行输入两个数,表示在第i-1个金矿需要的人手和可获得的金子数量。

对于每组数据,输出一个整数,表示能够获得的最大金子数量。
(保证数据在整形范围内)

输入样例:

   100 5

   77 92

   22 22

   29 87

   50 46

   99 90

输出样例:

   133

题目分析:

典型的01背包题目,我们来对比一下经典01背包题目描述:
有一个包和n个物品,包的容量为m,每个物品都有各自的体积和价值,问当从这n个物品中选择多个物品放在包里而物品体积总数不超过包的容量m时,能够得到的最大价值是多少?[对于每个物品不可以取多次,最多只能取一次,之所以叫做01背包,0表示不取,1表示取]
我们对于每一个物品,只有放进包中或者不放进包中两种情况,而本题金矿也是,派足够的人或者一个人也不派。
我们用动态规划的思维来看这个问题。看最后一块金矿n 。
1,假设我们选择开采,得到的金子为用所有人数m减去最后一个金矿所需人数people[n]去开采第1~n-1号金矿最优解加上最后一块金矿获得金子gold[n]。
2,假设我们选择不开采,得到的金子为用所有人数m去开采1~n-1号金矿。
那么我们只需要判断1,2这两种情况哪个是最优的就可以得出最后的答案。
我们对最后一块金矿思考完毕,对倒数第二块,倒数第三块,一直到第一块金矿的思考也相类似。
这样我们能够得到一个方程:
dp[m][n]=max(dp[m-1][n],dp[m-1][n-people[n]]+gold[n]);
其中dp[m][n]表示在n号金矿投入m人能得到的最大金子。
如果牺牲时间复杂度来获得空间复杂度需要对每个人进行遍历,得到的dp方程为:
dp[m]=max(dp[m],dp[m-people[n]]+gold[n]);
dp[m]表示在动用m个人的情况下获得的最大金子。

代码如下:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

int dp[105][10005];
int people[105],gold[105];


int getdp(int p,int m)
{
    int ret;
    if (dp[p][m]!=-1)
    {
        ret=dp[p][m];
    }
    else if (m==1)
    {
        if (p>=people[m]) ret=gold[m];
        else ret=0;
    }
    else if (p>=people[m])
    {
        ret=max(getdp(p-people[m],m-1)+gold[m],getdp(p,m-1));
    }
    else ret=getdp(p,m-1);
    dp[p][m]=ret;
    return ret;
}

int main()
{
    int PeopleNum,MineNum;
    while(scanf("%d%d",&PeopleNum,&MineNum)!=-1)
    {
        for(int i=1;i<=MineNum;i++)
        {
            scanf("%d%d",&people[i],&gold[i]);
        }
        memset(dp,-1,sizeof(dp));
        cout<<getdp(PeopleNum,MineNum)<<endl;
    }
    return 0;
}

在这个的代码中,我用递归方法求答案,也可以选择循环遍历方式求得答案。下面提供这个更一般的遍历方法。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

using namespace std;

int GoldNum,PeopleNum;
int people[10005],gold[105];
int dp[10005];


int main()
{
    while(~scanf("%d%d",&PeopleNum,&GoldNum))
    {
        for(int i=1;i<=GoldNum;i++)
        {
            scanf("%d%d",&people[i],&gold[i]);
        }
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=GoldNum;i++)
        {
            for(int j=PeopleNum;j>=people[i];j--)
            {
                dp[j]=max(dp[j],dp[j-people[i]]+gold[i]);
            }
        }
        printf("%d\n",dp[PeopleNum]);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值