混合背包
分别处理就好啦
二维费用的背包问题
要多一维数组,其余和前三种基本一样
洛谷p1509找GF(tan90)
除了二维以外还有一个最大数量时求最小花费的限制,先是用结构体写了一个最简陋的版本(然后发现题解也没有更优化的版本......)
把妹子看成有两种花费(money rp)的物品(这样会被打的吧),然后dp
#include<bits/stdc++.h>
using namespace std;
struct girl
{
int money,rp,time;
};
girl g[1010];
struct vir
{
int num,cost;
};
vir s[1010][1010];
int n,m,r;
void init()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&g[i].money,&g[i].rp,&g[i].time);
scanf("%d%d",&m,&r);
return;
}
bool check(int i,int j,int k)
{
if(s[i-g[k].money][j-g[k].rp].num+1>s[i][j].num)return true;//如果选上后人数更多
if(s[i-g[k].money][j-g[k].rp].num+1==s[i][j].num&&
s[i-g[k].money][j-g[k].rp].cost+g[k].time<s[i][j].cost)return true;//如果选上后人数一样但是时间更少
return false;
}
void dp()
{
for(int i=0;i<=m;i++)
for(int j=0;j<=r;j++)
s[i][j].num=0,s[i][j].cost=0;
for(int k=1;k<=n;k++)//两个费用的01背包
for(int i=m;i>=g[k].money;i--)
for(int j=r;j>=g[k].rp;j--)
if(check(i,j,k))//如果选这个妹子更优
{
s[i][j].num=s[i-g[k].money][j-g[k].rp].num+1;
s[i][j].cost=s[i-g[k].money][j-g[k].rp].cost+g[k].time;
}
return;
}
void print()
{
if(s[m][r].num==0)//OIer是没有妹子的哈哈哈哈哈哈哈嗝
{
printf("0");
return;
}
printf("%d",s[m][r].cost);
return;
}
int main()
{
init();
dp();
print();
return 0;
}
分组背包
洛谷p1757通天之分组背包
具体看代码
#include<bits/stdc++.h>
using namespace std;
int m,n,s[1010]={0},temp=0;
struct subject
{
int weight,value,team;
};
subject a[1010];
int main()
{
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&a[i].weight,&a[i].value,&a[i].team);
if(a[i].team>temp)temp=a[i].team;
}
for(int t=1;t<=temp;t++)//组别在前保证每一组都分别被计算,不同组互不影响
for(int i=m;i>0;i--)//容量,倒序保证01背包
for(int j=1;j<=n;j++)//容量在前保证该容量尝试了该组中的每一个物品且没有选择多个物品
if(a[j].team==t&&i-a[j].weight>=0)
s[i]=max(s[i],s[i-a[j].weight]+a[j].value);
printf("%d",s[m]);
return 0;
}
有依赖的背包
洛谷p1064金明的预算方案
可以简单模拟选取方案,01背包过(pascal时期也是这么写的),但是为了严谨,还是敲了这个出来:
#include<bits/stdc++.h>
using namespace std;
int n,m,s[40100]={0},t[40100]={0};
struct subject
{
int weight,value,team;
};
subject a[70];
void init()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a[i].weight,&a[i].value,&a[i].team);
a[i].value*=a[i].weight;
}
return;
}
void dp()
{
for(int i=1;i<=m;i++)
if(a[i].team==0)//如果是主物品
{
for(int j=1;j<=a[i].weight;j++)t[j]=0;//t数组存这组物品选择后可达到的最大值
for(int j=a[i].weight;j<=n;j++)t[j]=s[j-a[i].weight]+a[i].value;//先要全选k物品
for(int j=1;j<=m;j++)
if(a[j].team==i)//如果是k这一组
for(int k=n;k>=a[i].weight+a[j].weight;k--)//注意下界
t[k]=max(t[k],t[k-a[j].weight]+a[j].value);//在a的基础上01背包
for(int j=a[i].weight;j<=n;j++)//如果该组选择后比当前值大
if(t[j]>s[j])s[j]=t[j];
}
return;
}
int main()
{
init();
dp();
printf("%d",s[n]);
return 0;
}
ps:修改程序要前后一致,脑子发涨造成for(int j=a[i].weight;j<=n;i++),卡了好一会儿......
多人背包
洛谷p1858多人背包
需要多一维来存储前k个最优解的序列,与01背包相似
#include<bits/stdc++.h>
using namespace std;
int k,v,n,s[5010][60],ans=0,temp[60];
struct subject
{
int w,val;
};
subject a[110];
void init()
{
scanf("%d%d%d",&k,&v,&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i].w,&a[i].val);
return;
}
void dp()
{
for(int i=1;i<=n;i++)//真-01背包模版
for(int j=v;j>=a[i].w;j--)
{
int x=1,y=1,t=1;
while(t<=k)//选取k个最优解,temp存储防止覆盖
{
if(s[j][x]>=s[j-a[i].w][y]+a[i].val)
temp[t]=s[j][x],x++;
else temp[t]=s[j-a[i].w][y]+a[i].val,y++;
t++;
}
for(int x=1;x<=k;x++)s[j][x]=temp[x];
}
return;
}
void print()
{
for(int i=1;i<=k;i++)
ans+=s[v][i];
printf("%d",ans);
return;
}
int main()
{
init();
for(int i=0;i<=v;i++)
for(int j=0;j<=k;j++)s[i][j]=-1000000;//该题要求装满,所以初始化如此
s[0][1]=0;
dp();
print();
return 0;
}