DP-01背包变式题

变式1:最优均分问题

题目1 CSL分苹果

登录—专业IT笔试面试备考平台_牛客网
CSL手上有n个苹果,第i个苹果的质量是wi,现在他想把这些苹果分给他的好朋友wavator和tokitsukaze。但是CSL为了不让他们打架,根据质量决定尽量地均分成两堆分给他们。现在CSL想知道到底给每个人分多少质量的苹果。
注意:苹果不能劈开来,并且如果不能正好均分,tokitsukaze小姐姐会拿到重的那一堆。

解题

要得到质量最接近的两堆苹果,其实就是求出可以拼出的质量小于sum/2的最大值。

#include
#include
using namespace std;
const int N=110;
int n,w[N];
int f[N*N];
int main()
{
    scanf("%d",&n);
    int m=0;
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&w[i]);
        m+=w[i];
    }
    
    for(int i=1;i<=n;i++)
        for(int j=m/2;j>=w[i];j--)
            f[j]=max(f[j],f[j-w[i]]+w[i]);
   
    if(f[m/2]>m-f[m/2]) printf("%d %d",m-f[m/2],f[m/2]);
    else printf("%d %d",f[m/2],m-f[m/2]);
}

题目2 失衡天平

登录—专业IT笔试面试备考平台_牛客网
终于Alice走出了大魔王的陷阱,可是现在傻傻的她忘了带武器了,这可如何是好???这个时候,一个神秘老人走到她面前答应无偿给她武器,但老人有个条件,需要将所选武器分别放在天平的两端,若天平平衡则可以将天平上的所有武器拿走,还好这个天平锈迹斑斑,只要两端重量相差小于等于m就会保持平衡,Alice傻傻的认为越重的武器越好,求Alice最多能拿走的武器总重量。(不限操作次数)

解题

如果按照背包问题的传统思路,设置总花费为j,是做不下去的,因为我们的目的不仅仅是均分重量,而是要求出两堆物品重量在一定差值范围内的最优解。

本题中应该将两堆物品重量差值设置为j进行dp。

我个人在写代码时容易忽略的点是,要注意将初始状态设置为负无穷。因为在dp过程中,w[i]的值可能会比最优解要大,在f[i][j]=max(f[i][j],f[i-1][abs(j-w[i])]+w[i])的状态转移过程中,我们有可能用不合法的0+w[i]的状态把最优解转移走。

dp思路如下:

DP-01背包变式题_第1张图片

#include
#include
using namespace std;
const int N=110;
int n,m;
int w[N];
int f[N][2*N];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    memset(f,-0x3f,sizeof(f));
    f[0][0]=0;
    
    for(int i=1;i<=n;i++)
        for(int j=0;j<=N;j++)
        {
            f[i][j]=max(f[i][j],f[i-1][abs(j-w[i])]+w[i]);
            f[i][j]=max(f[i][j],f[i-1][j+w[i]]+w[i]);
            f[i][j]=max(f[i][j],f[i-1][j]);
        }
   
    int res=0;
    for(int i=0;i<=m;i++) res=max(res,f[n][i]);
    printf("%d",res);
}

变式2:不止2维的01背包

题目1 队伍配置

登录—专业IT笔试面试备考平台_牛客网
萌学姐在玩大型手游《futa go》,他现在准备进入作战环节,所以他准备安排自己的队伍。
队伍配置里,可供玩家选择的作战人物被称作“从者”,玩家可以对每个“从者”可以装备至多1件的“概念礼装”,玩家具有一个cost上限值。详细定义如下:
1、    每个从者和概念礼装都具有攻击值ATK。
2、    每个从者和概念礼装都会占据一定的cost值。
3、    每个从者和概念礼装只能上场一次,不能重复使用。
4、    概念礼装只能装备在从者上,不能单独存在。
5、    选择的从者和概念礼装的cost值之和不能超过玩家的cost上限值。
6、    最多可以选择5名从者(在cost值限制下)。
现在给出玩家仓库的每个从者和每件概念礼装的ATK值和cost值,问在满足定义的条件下,队伍可以凑出的最大ATK值

解题

和传统的01背包比较,本题要求我们在从者和概念礼装两种物品中选,而且需要控制①从者数量小于5,②概念礼装数量不多于从者数量。也就是说,我们的01背包过程多了两个维度:从者数量和概念礼装数量。

我们可以进行两次01背包,f1对从者进行,f1(i,j,k)表示只在前i个从者中考虑,cost不大于j,总共选择了k个从者;f2对概念礼装进行,f2(i,j,k)表示只在前i个概念礼装中考虑,cost不大于j,总共选择了k个概念礼装。f1和f2都可以优化到2维来进行。

状态计算为:

DP-01背包变式题_第2张图片

最后遍历两个数组,求出最优组合。

#include
using namespace std;
const int N=310,M=140;
typedef pair PII;
#define w first
#define v second
int n,m,d;
PII congzhe[N],zhuangbei[N];
int f1[M][10],f2[M][10];
int main()
{
    scanf("%d%d%d",&n,&m,&d);
    congzhe[0]={0,0};
    zhuangbei[0]={0,0};
    for(int i=1;i<=n;i++) scanf("%d%d",&congzhe[i].w,&congzhe[i].v);
    for(int i=1;i<=m;i++) scanf("%d%d",&zhuangbei[i].w,&zhuangbei[i].v);
    int cnt=0;
    for(int i=1;i<=n;i++)
        for(int j=d;j>=congzhe[i].v;j--)
            for(int k=0;k<=min(5,i);k++)
            {
                if(k>0) f1[j][k]=max(f1[j-congzhe[i].v][k-1]+congzhe[i].w,f1[j][k]);
            }
    for(int i=1;i<=m;i++)
        for(int j=d;j>=zhuangbei[i].v;j--)
            for(int k=0;k<=min(5,i);k++)
            {
                if(k>0) f2[j][k]=max(f2[j-zhuangbei[i].v][k-1]+zhuangbei[i].w,f2[j][k]);
            }
    int res=0;
    for(int i=0;i<=min(5,n);i++)
        for(int j=0;j<=min(i,m);j++)
            for(int j1=0;j1<=d;j1++)
                for(int j2=0;j2+j1<=d;j2++)
            res=max(res,f1[j1][i]+f2[j2][j]);
    printf("%d",res);
}

你可能感兴趣的:(动态规划,算法,动态规划,c++)