Codeforces366 C. Dima and Salad (01背包)

题意:

有n个(a,b)二元组
要求从中选出若干个二元组
满足sum(a(i))/sum(b(i))=k,其中sum(a(i))表示选中的二元组中a(i)的和,sum(b(i))同理,且式子左边需要整除
在满足该式子的同时,使得sum(a(i))尽量大,输出sum(a(i))的最大值

数据范围:n,a(i),b(i)<=100,k<=10

解法:
sum(a[i])/sum(b[i])=k
显然要先移项变为sum(a[i])=sum(b[i])*k
继续移项变为sum(a[i])-sum(b[i])*k=0
->sum(a[i])-sum(k*b[i])=0
->sum(a[i]-k*b[i])=0,同时需要sum(a[i])最大
问题可以转化为n个物品,每个物品的重量为a[i]-k*b[i],价值为a[i]
容量为0的背包恰好装满所能得到的最大价值,是一个01背包问题

因为a[i]-k*b[i]可能是负数,可以给数组设置一个偏移值避免越界
也可以将正负重量分开计算,f[i]为容量i的最大价值,g[i]为容量-i的最大价值
负数重量的物品重量取反之后在dp就行了,最后的答案为f[i]+g[i]的最大值
代码里面用的是正负重量分开计算的

注意背包必须装满,因此数组需要初始化为-inf

ps:推出来的式子右边为0,一时间想不到居然有容量为0的背包,还是太年轻了
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=105;
vector<pair<int,int> >aa,bb;
int f[10000+5],g[10000+5];//f[i]表示容量i的最大值,g[i]表示容量-i的最大值
int a[maxm],b[maxm];
int n,k;
signed main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)cin>>b[i];
    for(int i=1;i<=n;i++){
        int x=a[i]-k*b[i];
        if(x>=0)aa.push_back({x,a[i]});//正
        else bb.push_back({-x,a[i]});//负
    }
    for(int i=1;i<=10000;i++)f[i]=g[i]=-1e9;//背包必须装满,因此初始化为-inf
    f[0]=g[0]=0;
    for(auto i:aa){
        int v=i.first,w=i.second;
        for(int j=10000;j>=v;j--){
            f[j]=max(f[j],f[j-v]+w);
        }
    }
    for(auto i:bb){
        int v=i.first,w=i.second;
        for(int j=10000;j>=v;j--){
            g[j]=max(g[j],g[j-v]+w);
        }
    }
    int ans=0;
    for(int i=0;i<=10000;i++){
        ans=max(ans,f[i]+g[i]);
    }
    if(ans==0)ans=-1;
    cout<<ans<<endl;
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值