背包问题相关

47 篇文章 1 订阅
1 篇文章 0 订阅

Google Kickstart2019 Round B Problem B能量石
链接:https://www.acwing.com/problem/content/736/

类似玩杂质的牛与国王游戏

如果最终方案中相邻的两石头为
l[i]/s[i]<l[j]/s[j]
l[i]*s[j]<s[i]*l[j]

则这两石头的收益和
ei-(s[1]+…s[i-1])*li+ej-(s[1]+…s[i])*lj
如果交换它们
ej-(s[1]+…s[i-1])*lj+max(0,ei-(s[1]+…+s[i-1]+s[j])*li)

上面两式最后可以整理成
获得s[j]*l[i]
获得s[i]*l[j]

则矛盾,故最终答案一定是L[i]/s[i]>=L[j]/S[j],
移项L[i]*S[j]>=L[j]*S[i]
如果为相等的情况,是否会出现先完成后面的再完成前面的情况,
类似上面的推理,可以发现交换完之后答案不会变坏,故把所有石头按这个规则排完序,最终答案一定可以通过这个顺序dp得到。直接dp即可。

#include<bits/stdc++.h>
using namespace std;
const int N=105;
struct arr{
    int s,e,l;
}a[N];
int T,n,f[N][N*N];
int cmp(arr A,arr B){
    return A.s*B.l<A.l*B.s;
}
int main(){
    cin>>T;
    for(int t=1;t<=T;t++){
        cin>>n;
        int m=0;
        for(int i=1;i<=n;i++)cin>>a[i].s>>a[i].e>>a[i].l,m+=a[i].s;
        sort(a+1,a+n+1,cmp);
        for(int i=1;i<=n;i++){
            for(int j=0;j<=m;j++){
                f[i][j]=f[i-1][j];
                if(j>=a[i].s){
                    int pro=a[i].e-(j-a[i].s)*a[i].l;
                    if(pro>0)f[i][j]=max(f[i][j],f[i-1][j-a[i].s]+pro);
                }
            }
        }
        int ans=0;
        for(int i=0;i<=m;i++)ans=max(ans,f[n][i]);
        printf("Case #%d: %d\n",t, ans);
    }
    return 0;
}


2967 [USACO09DEC]Video Game Troubles G
链接:https://www.luogu.com.cn/problem/P2967
对于每个游戏机,可以单独考虑
每次将前i-1个游戏机单独拷贝出来,然后在现有基础上把i这个游戏机加上,对于每个游戏进行背包dp,之后与前面的进行更新即可。

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n,m,money,n1,x,y;
int f[N],g[N];
int main(){
    cin>>n>>m;
    while(n--){
        cin>>money>>n1;
        for(int i=money;i<=m;i++)g[i]=f[i-money];
        while(n1--){
            cin>>x>>y;
            for(int j=m;j>=money+x;j--)g[j]=max(g[j],g[j-x]+y);
        }
        for(int i=0;i<=m;i++)f[i]=max(f[i],g[i]);
    }
    cout<<f[m]<<endl;
    return 0;
}

有依赖的背包问题
链接:https://www.acwing.com/problem/content/10/

树上背包dp,f[i][j]表示以i为根的子树选j个点最大为多少。

#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,v,a[N],b[N],root,x;
vector<int>e[N];
int f[N][N];
void dfs(int x){
    for(int i=0;i<e[x].size();i++){
        dfs(e[x][i]);
        for(int j=v-a[x];j>=0;j--){
            for(int k=0;k<=j;k++){
                f[x][j]=max(f[x][j],f[x][j-k]+f[e[x][i]][k]);
            }
        }
    }
    for(int i=v;i>=a[x];i--)f[x][i]=f[x][i-a[x]]+b[x];
    for(int i=0;i<a[x];i++)f[x][i]=0;
}
int main(){
    cin>>n>>v;
    for(int i=1;i<=n;i++){
        cin>>a[i]>>b[i]>>x;
        if(x==-1)root=i;
        else e[x].push_back(i);
    }
    dfs(root);
    cout<<f[root][v]<<endl;
    return 0;
}

1776 宝物筛选
链接:https://www.luogu.com.cn/problem/P1776
单调队列优化多重背包

#include<bits/stdc++.h>
using namespace std;
const int N=40005;
int n,m,v,w,s,h,t,q[N];
int f[N],g[N];
int main(){
    cin>>n>>m;
    while(n--){
        cin>>w>>v>>s;
        memcpy(g,f,sizeof(f));
        for(int i=0;i<v;i++){
            h=0;t=-1;
            for(int j=i;j<=m;j+=v){
                if(h<=t&&(j-q[h])/v>s)h++;
                while(h<=t&&g[q[t]]+(j-q[t])/v*w<=g[j])t--;
                q[++t]=j;
                f[j]=g[q[h]]+(j-q[h])/v*w;
            }
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值