前后缀背包例题两道

例题1:

出处:2023牛客寒假集训4


清楚姐姐最近学会了01背包,01背包是背包问题中最简单的问题。

01背包的约束条件是给定几种物品,每种物品有且只有一个,并且有权值和体积两个属性。在01背包问题中,因为每种物品只有一个,对于每个物品只需要考虑选与不选两种情况。

如果不选择将其放入背包中,则不需要处理。如果选择将其放入背包中,由于不清楚之前放入的物品占据了多大的空间,需要枚举将这个物品放入背包后可能占据背包空间的所有情况。

现在清楚姐姐有N个蝴蝶结,第i个蝴蝶结的体积为wi好看程度为vi,她准备了一个容量大小为M的包包。她可以从这N个蝴蝶结中任选若干个放入背包,但是所选蝴蝶结的体积总和不能大于背包的容量M,清楚姐姐想要让所选蝴蝶结的好看程度总和最大化。

她运用自己刚刚学会的01背包知识,快速算出了她能用她的包包装下蝴蝶结好看程度总和的最大值。

现在清楚姐姐有了一个新的问题,我们定义原问题的答案,即所选蝴蝶结好看程度总和的最大值为{max}Val

定义从这N个蝴蝶结中去掉第i个蝴蝶结后,从剩余N−1个蝴蝶结中任选若干个放入背包,所选蝴蝶结好看程度总和的最大值
val'。
若val'<valmax,则称第i个蝴蝶结为一个“必选蝴蝶结”。

清楚姐姐现在获得了调整蝴蝶结好看程度的机会,她想要知道,对于第i个蝴蝶结,在它初始好看程度的基础上,再加上多少,该蝴蝶结就能够成为一个“必选蝴蝶结”。

关键数据1-5e3

前后缀背包

#include<bits/stdc++.h>
using namespace std;
#define fp(i, a, b) for (int i=a;i<=b;++i)
#define fd(i, a, b) for (int i=a;i>=b;--i)
#define pii pair<int,int>
typedef long double db;
#define int long long
const int N=5010;
int n,m;
int v[N],w[N];
int dp1[N][N],dp2[N][N];
void solve()
{

    cin>>n>>m;

    fp(i,1,n)
    {
        cin>>v[i]>>w[i];
    }


    fp(i,1,n)
    {
        fp(j,0,m)
        {
            dp1[i][j]=dp1[i-1][j];
            if(j>=v[i])
            {
                dp1[i][j]=max(dp1[i][j],dp1[i-1][j-v[i]]+w[i]);
            }
        }
    }

    fd(i,n,1)
    {
        fp(j,0,m)
        {
            dp2[i][j]=dp2[i+1][j];
            if(j>=v[i])
            {
                dp2[i][j]=max(dp2[i][j],dp2[i+1][j-v[i]]+w[i]);
            }
        }
    }

    fp(i,1,n)
    {
        int mmax=0;

        for(int j=0; j<=m-v[i]; j++)
        {
            mmax=max(mmax,dp1[i-1][j]+dp2[i+1][m-v[i]-j]);
        }

        int mmmax=0;

        for(int j=0; j<=m; j++)
        {
            mmmax=max(mmmax,dp1[i-1][j]+dp2[i+1][m-j]);
        }

        cout<<max(0ll,mmmax-mmax+1-w[i])<<"\n";
    }


}
signed main()
{
    int T;

    T=1;

    //cin>>T;

    while(T--)
    {
        solve();
    }

    return 0;
}

例题2

出处:杭州icpc2022

C. No Bug No Game

time limit per test 1 second

memory limit per test 1024 megabytes

inputstandard input

outputstandard output

Putata is preparing the RPG Pro League (RPL) held by the International Computer Playing Company (ICPC). In this RPG game, the player will wear nn items at the same time. Each item can offer the player several points of power. There is a magic buff in the game, which can upgrade each item such that they can offer several points of bonus power.

However, the buff is limited, it can only buff at most kk points of power. Formally, assume the player is wearing nothing initially, and then will wear all the nn items one by one. The game server will scan through all these nn items one by one, according to the permutation that the player wears them. When the server is scanning the ii-th item, which can offer pipi points of power, let sum=∑1≤j<ipdenoting the total points of power scanned before:

  • If sum+pi≤k, the whole item will be upgraded. The buff will offer wi,piwi,pi points of bonus power.

  • If sum≥k, the item won't be upgraded. The buff will offer nothing.

  • Otherwise, only a part of the item will be upgraded. The buff will offer wi,k−sumwi,k−sum points of bonus power.

Putata is clever, he soon realized that he can adjust the permutation to wear these nn items to gain more points of bonus power! Unfortunately, Putata doesn't know the optimal permutation, please write a program to help him.

The behavior of the magic buff performed by the game server is a bug. The game code can work all thanks to bugs, so it is possible that wi,a>wi,b where a<b.

关键数据1-3e3

唯一不同是要初始化为负无穷,因为此处01背包的定义是恰好取到

#include<bits/stdc++.h>
using namespace std;
#define fp(i, a, b) for (int i=a;i<=b;++i)
#define fd(i, a, b) for (int i=a;i>=b;--i)
#define pii pair<int,int>
#define pb push_back
typedef long double db;
#define int long long
const int N=3010;
const int INF=0x3f3f3f3f;
int n,m;
int w[N];
int v[N][N];
int dp1[N][N];
int dp2[N][N];
void solve()
{
    cin>>n>>m;

    fp(i,1,n)
    {
        cin>>w[i];
        fp(j,1,w[i])
        {
            cin>>v[i][j];
        }
    }

    memset(dp1,-0x3f,sizeof dp1);
    memset(dp2,-0x3f,sizeof dp2);
    dp1[0][0]=0;
    dp2[n+1][0]=0;
    fp(i,1,n)
    {
        fp(j,0,m)
        {
            dp1[i][j]=dp1[i-1][j];
            if(j>=w[i])
            {
                dp1[i][j]=max(dp1[i][j],dp1[i-1][j-w[i]]+v[i][w[i]]);
            }
        }
    }

    fd(i,n,1)
    {
        fp(j,0,m)
        {
            dp2[i][j]=dp2[i+1][j];
            if(j>=w[i])
            {
                dp2[i][j]=max(dp2[i][j],dp2[i+1][j-w[i]]+v[i][w[i]]);
            }
        }
    }

    int mmax=-INF;

    fp(i,0,m)
    {
        mmax=max(mmax,dp1[n][i]);
    }

    fp(i,1,n)
    {
        for(int p=0; p<=w[i]; p++)
        {
            for(int j=0; j<=m-p; j++)
            {
                mmax=max(mmax,dp1[i-1][j]+v[i][p]+dp2[i+1][m-p-j]);
            }
        }
    }

    cout<<mmax<<"\n";
}
signed main()
{
    int T;

    T=1;

    //cin>>T;

    while(T--)
    {
        solve();
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值