背包与贪心

来源:互联网 发布:加工中心编程第一步 编辑:程序博客网 时间:2024/05/16 17:03

背包问题最经典的就是01背包。

n个物品,有体积 w 和价值 v 两种属性,选择一部分填充容量为 m 的背包(可以不填满),求能获得的最大价值。

其实分析下来,动态转移方程就是 f[i][v]=max{f[i-1][v], f[i-1][v-c[i]]+w[i]} ,优化后就是 f[v]=max{f[v], f[v-c[i]]+w[i]}  (第二重for循环从m -> v[i],为了不更新i-1层的状态)

这个状态转移方程就会一个贪心过程,不断的用第i个物品更新每个f[j]的值,找到最大的f[j],最终的结果就是最大值。

其实背包除了不一定恰好装满,恰好装满,还有大于等于背包的容量这种状态。像这种大于背包容量的最优解,不会求最大值(求最大值可以一直贪,无穷大),会求大于背包容量的最小花费(中文其实挺好玩的,求最大就说获得的价值,求最小就说是花费),这是一个零界点,具有最优性质在里面,所以用贪心去优化背包。


看一道2017年百度之星的题:

度度熊与邪恶大魔王


Problem Description

度度熊为了拯救可爱的公主,于是与邪恶大魔王战斗起来。

邪恶大魔王的麾下有n个怪兽,每个怪兽有a[i]的生命值,以及b[i]的防御力。

度度熊一共拥有m种攻击方式,第i种攻击方式,需要消耗k[i]的晶石,造成p[i]点伤害。

当然,如果度度熊使用第i个技能打在第j个怪兽上面的话,会使得第j个怪兽的生命值减少p[i]-b[j],当然如果伤害小于防御,那么攻击就不会奏效。

如果怪兽的生命值降为0或以下,那么怪兽就会被消灭。

当然每个技能都可以使用无限次。

请问度度熊最少携带多少晶石,就可以消灭所有的怪兽。

Input

本题包含若干组测试数据。

第一行两个整数n,m,表示有n个怪兽,m种技能。

接下来n行,每行两个整数,a[i],b[i],分别表示怪兽的生命值和防御力。

再接下来m行,每行两个整数k[i]和p[i],分别表示技能的消耗晶石数目和技能的伤害值。

数据范围:

1<=n<=100000

1<=m<=1000

1<=a[i]<=1000

0<=b[i]<=10

0<=k[i]<=100000

0<=p[i]<=1000

Output
对于每组测试数据,输出最小的晶石消耗数量,如果不能击败所有的怪兽,输出-1


这是一道完全背包,但是是n个背包,怪兽的生命力不同,防御值也不同,所以没有办法很好的去建立一个模型。

但是生命值1000种,防御值为10种,一共是10000种怪兽,我们求出这10000种怪兽的花费,在乘以该种类怪兽的数量,累加就是答案。这题的极限应该是O(1e9),但是数据应该不卡时间,所以200ms多就过了。

首先对m种技能进行排序(贪心策略),按防御力从小到大进行排序,防御力相同,花费大的在前(从劣往优排序),两重for,枚举怪物的种类,然后用m种物品更新最小花费。代码如下:

#include <iostream>#include <stdio.h>#include <string.h>#include <algorithm>#define LL long long#define P pair<int,int>using namespace std;const int maxn=1e5+100,maxm=1e4+10;int a[maxn],b[maxn];LL f[maxm];P c[maxm];int n,m,ma,mb;namespace std{bool operator < (const pair<int,int> &t,const pair<int,int> &s){    return t.second==s.second?(t.first>s.first):(t.second<s.second);}}LL solve(){    int t=1;    for(int i=2; i<=m; ++i)    {        while(t>0&&c[t].first>c[i].first) --t;        c[++t]=c[i];    }    LL ans=0;    for(int i=0; i<=mb; ++i)    {        for(int j=1; j<=ma; ++j)        {            f[j]=1ll*j*c[t].first;            for(int k=1; k<=t; ++k)                if(c[k].second>i)                    f[j]=min(f[j],f[max(0,j+i-c[k].second)]+c[k].first);        }        for(int j=1; j<=n; ++j)            if(b[j]==i) ans+=f[a[j]];    }    return ans;}int main(){    while(scanf("%d%d",&n,&m)!=EOF)    {        ma=0,mb=0;        for(int i=1; i<=n; ++i)        {            scanf("%d%d",&a[i],&b[i]);            if(a[i]>ma) ma=a[i];            if(b[i]>mb) mb=b[i];        }        for(int i=1; i<=m; ++i)            scanf("%d%d",&c[i].first,&c[i].second);        sort(c+1,c+m+1);        if(c[m].second<=mb)        {            printf("-1\n");            continue;        }        /*                for(int i=1;i<=m;++i)                    printf("%d %d\n",c[i].first,c[i].second);        */        printf("%I64d\n",solve());    }    return 0;}




原创粉丝点击