HDU 2602 Bone Collector
题意:给n个物品和一个体积为w的背包,每个物品的体积和价值已知,问这个包最多能装多少价值的物品
分析;对于每个物品来说,有两种状态,装or不装。直接暴力?来看看数据范围,n<=1000,gg….如果n在25左右,还可以考虑一下。
那我们考虑一下打牌~
如果状态是dp[i][j]:前i个物品,拿j个。
那么答案就是max(dp[n])
首先看三个条件,符合.
但是,好像漏了些啥。体积!对了还有体积这个限制条件。那么还需要把当前状态的体积记录下来呀,咋记录,因为dp[i][j]这一个状态的体积可能不止一种呀。。
那么换一个想法,物品的个数我们不需要知道,我们就用体积来转移
状态为 dp[i][v]:前i个物品 体积为v时的最大价值
状态转移方程:
if(v-w[i]>=0)
dp[i][j]=max(dp[i-1][j],dp[i-1][v-w[i]]);
else dp[i][j]=dp[i-1][j];
时间 n*m,空间n*m。能不能改良一下?时间好像改不了,空间好像可以。第i个物品的状态是由第i-1个物品的状态转移过来的,可以用滚动数组,空间为n*2。能不能只用一维呢?行。d[i][j]只能由dp[i-1][j]和dp[i-1][v-w[i]]转移过来,j和j-w[i]都不大于j,所以在求出了前i-1个物品所有状态后,求前i个物品的时候,从后开始转移。
dp[i]:体积为i的最大价值
for(int i=1;i<=n;i++){
for(int j=m;j>=0;j--)
{
if(j>=w[i])
dp[j]=max(dp[j],dp[j-w[i]]);
}
}
饭卡 HDU - 2546
题意:
电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
和前面的问题好像啊~直接出牌
状态为 dp[i][j]:前i种菜,卡中的钱为j,最后卡中最少的余额是多少
ans=dp[n][v];
状态转移
for(int i=1;i<=n;i++)
{
for(int j=0;j<=mon;j++)
{
if(dp[i-1][j]>=5)
{
if(j>=m[i])
dp[i][j]=min(dp[i-1][j],dp[i-1][j-m[i]]);
else dp[i][j]=dp[i-1][j]-m[i];
}
else dp[i][j]=dp[i-1][j]
}
}
这样状态转移对么?
如果样例是
2个菜,菜的价格分别是3 100,饭卡中的余额为5
那么按照上面的状态转移
dp[1][5]=2,其他dp[1]都为0
dp[2][5]=2;
可是这里先买100,是最优解呀。。为什么会出现这种情况呢?
因为这个方程,只买第i种物品应该只与j有关,加个
if(j>=5) dp[i][j]=min(j-p[i],dp[i][j]);
for(int j=0;j<=v;j++) dp[0][j]=j;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=v;j++)
{
if(dp[i-1][j]>=5)
{
if(j>=p[i])
dp[i][j]=min(dp[i][j],min(dp[i-1][j],dp[i-1][j-p[i]]));
else dp[i][j]=min(dp[i][j],dp[i-1][j]-p[i]);
}
else dp[i][j]=dp[i-1][j];
if(j>=5)
dp[i][j]=min(dp[i-1][j],j-p[i]);
}
}
还是wa,为啥?
dp[i][j]的状态会由
dp[i-1][j](第i种菜不取),
if(dp[i-1][j]>==5)
dp[i-1][j-p[i]](取第i种菜,第i种菜完全买下来),dp[i-1][j]-p[i](取第i种菜,第i种菜不完全买下来)
if( z)
j-p[i](只取第i种菜)转移过来
哪里错了呢。。。
来一组样例
3
2 1 11
6
程序运行出来的是-5;
因为dp[i-1][6]如果小于5,那么之后卡钱为6的状态只能由dp[i-1][6]和6-p[i]转移。
像这样之后还有限制的,就先把限制去掉后再来打牌…
如果大于或等于5,就先把5留下去买最贵的。其他就用01背包来写
01背包的时候和顺序是没有关系的啊~所以排不排序无所谓,但是最贵的要去掉,排完序后可以直接去掉最后一个。
用一个不排序的(来表示排序不重要:>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 1004;
int p[maxn];
int dp[maxn][maxn];
int main()
{
int n,v;
while(scanf("%d",&n)!=EOF&&n){
memset(p,0,sizeof(p));
int maxx=0,k=0;
for(int i=1;i<=n;i++){
scanf("%d",&p[i]);
if(p[i]>maxx) {maxx=p[i];k=i;}
}
for(int i=k;i<=n;i++)
p[i]=p[i+1];
scanf("%d",&v);
memset(dp,0,sizeof(dp));
if(v<5) printf("%d\n",v);
else {
v-=5;
for(int i=1;i<=n-1;i++)
{
for(int j=0;j<=v;j++)
{
dp[i][j]=dp[i-1][j];
if(j>=p[i])
dp[i][j]=max(dp[i][j],dp[i-1][j-p[i]]+p[i]);
}
}
printf("%d\n",v-dp[n-1][v]+5-maxx);
}
}
return 0;
}
hdu 1171
题意,有一堆物品,给你每种物品的个数和价值,要求尽可能将这些物品分成A B 两堆价值相等,然后不等的话,A要比B大些。输出A,B价值
分析:直接打牌
01背包模板题嘛,背包体积为总价值/2,算出来是B的,用总-B的为A的.
完。
Codeforces 864E
题意:给n个物品,每个物品有三个值,t,d,v(t:保存这个物品需要的时间,d:在d时刻之前保存这个物品可以获得v价值,否则没有价值),问保存哪些,可获得最多价值
分析:直接出牌。可是这个和一般的01背包有点不一样,每个物品都有限制。
状态 dp[i][t]:前i个物品时间为t的时候,最大价值
那么按照d排个序,然后再01背包
这里需要输出路径
可以这样,但是要注意,这里的r和c是第一次出现的那个
while(r&&c)
{
if(c-p[r].t>=0&&dp[r][c]==dp[r-1][c-p[r].t]+p[r].v)
{
ans[h++]=r;
c=c-p[r].t;
}
r--;
}
打牌过程: (代码中会有很多细节啊~
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 114;
struct node
{
int t,d,v,id;
}p[maxn];
bool cmp(node a1,node a2)
{
if(a1.d!=a2.d)
return a1.d<a2.d;
else return a1.v>a2.v;
}
int dp[maxn][2105],ans[maxn];
int main()
{
int n,maxx=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d %d %d",&p[i].t,&p[i].d,&p[i].v);
p[i].id=i;
maxx=max(maxx,p[i].d);}
sort(p+1,p+n+1,cmp);
memset(dp,0,sizeof(dp));
int total=-1;//这里total要初始化为-1,不能为0,否则会RE,因为有为0的情况。
int r=0,c=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=maxx;j++)//在[0,p[i].t)区间中,dp[i][j]=dp[i-1][j]
dp[i][j]=dp[i-1][j];
for(int j=p[i].t;j<p[i].d;j++)
dp[i][j]=max(dp[i][j],p[i].v+dp[i-1][j-p[i].t]);
for(int j=p[i].d;j<=maxx;j++)
dp[i][j]=dp[i][j-1];
}
for(int i=0;i<=maxx;i++)//r,c是第一次出现的,也就是恰好为
{
if(dp[n][i]>total)
{
c=i;
total=dp[n][i];
}
}
int h=0;
r=n;
while(r&&c)//寻找路径
{
if(c-p[r].t>=0&&dp[r][c]==dp[r-1][c-p[r].t]+p[r].v)
{
ans[h++]=r;
c=c-p[r].t;
}
r--;
}
sort(ans,ans+h);
for(int i=0;i<h;i++)
ans[i]=p[ans[i]].id;
printf("%d\n",dp[n][p[n].d-1]);
cout<<h<<endl;
for(int i=0;i<h;i++)
{
if(i!=0) printf(" ");
printf("%d",ans[i]);
}
printf("\n");
return 0;
}