作为一个oier,以及大学acm党背包是必不可少的一部分。好久没做背包类动规了。久违地练习下-。-

dd__engi的背包九讲:http://love-oriented.com/pack/

鸣谢http://blog.csdn.net/eagle_or_snail/article/details/50987044,这里有大部分比较有趣的dp练手题。

hud 2602 01背包板子题

 #include<cstdio>
#include<iostream>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
using namespace std;
struct node
{
int cost,val;
}bag[];
int dp[];
int main()
{
int T,n,m;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
clr(dp);
clr(bag);
for(int i=;i<=n;i++)
scanf("%d",&bag[i].val);
for(int i=;i<=n;i++)
scanf("%d",&bag[i].cost);
for(int i=;i<=n;i++)
for(int j=m;j>=bag[i].cost;j--)
{
if(dp[j]<dp[j-bag[i].cost]+bag[i].val)
dp[j]=dp[j-bag[i].cost]+bag[i].val;
}
printf("%d\n",dp[m]);
}
return ;
}

01背包

hdu 2844 Coins 多重背包

就是一个10w的多重背包,每个物品的cost同时也作为value去做背包,我们求的是每个容量下的价值,所以没法做背包九讲里提到的对最终目标的常数优化。那么我们做完以后,统计一下背包里dp[i]==i的空间i总数即为答案。

 #include<cstdio>
#include<iostream>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
#define MAXN 100010
using namespace std;
int dp[MAXN];
struct node
{
int num,cost;
}good[MAXN];
int n,m,s,t,l,k,ans;
int max(int a,int b)
{
return a>b?a:b;
}
void zeroonepack(int cost,int val)
{
for(int i=m;i>=cost;i--)
{
dp[i]=max(dp[i],dp[i-cost]+val);
}
return ;
}
void completepack(int cost,int val)
{
for(int i=cost;i<=m;i++)
{
dp[i]=max(dp[i],dp[i-cost]+val);
}
return ;
}
void multipack(int cost,int val,int num)
{
if(cost*num>=m)
{
completepack(cost,val);
}
else
{
int t=;
while(t<=num)
{
zeroonepack(t*cost,t*val);
num-=t;
t*=;
}
zeroonepack(num*cost,num*val);
}
return ;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF && m!= && n!=)
{
clr(dp);
for(int i=;i<=n;i++)
{
scanf("%d",&good[i].cost);
}
for(int i=;i<=n;i++)
{
scanf("%d",&good[i].num);
}
for(int i=;i<=n;i++)
{
multipack(good[i].cost,good[i].cost,good[i].num);
}
ans=;
for(int i=;i<=m;i++)
{
if(dp[i]==i)
ans++;
}
printf("%d\n",ans);
}
return ;
}

多重背包

hdu 2159 FATE 完全背包

一个二维完全背包,将消耗忍耐度和杀怪数作为二维的cost,然后将求解每个忍耐度和杀怪数下经验值的最大值作为最大价值。我们做一遍二维完全背包,然后找杀怪数满(即dp[s][i])时最小的经验值超过升级所需经验值n的i,这个i即为最小忍耐度,然后m-i即为答案。

 #include<cstdio>
#include<iostream>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
#define MAXN 110
using namespace std;
int dp[MAXN][MAXN],v[MAXN],c[MAXN];
int n,m,k,s,t,l,ans,minn;
int max(int a,int b)
{
return a>b?a:b;
}
int main()
{
while(scanf("%d%d%d%d",&n,&m,&k,&s)!=EOF)
{
clr(dp);
for(int i=;i<=k;i++)
{
scanf("%d%d",&v[i],&c[i]);
}
for(int i=;i<=k;i++)
{
for(int j=;j<=s;j++)
{
for(int t=c[i];t<=m;t++)
dp[j][t]=max(dp[j][t],max(dp[j-][t],dp[j-][t-c[i]]+v[i]));
}
}
if(dp[s][m]<n)
{
printf("-1\n");
}
else
{
minn=m;
for(int i=;i<=m;i++)
if(dp[s][i]>=n && i<minn)
{
minn=i;
break;
}
printf("%d\n",m-minn);
}
}
return ;
}

二维完全背包

poj 1170 Shopping Offers 状压+完全背包

最多五个物品,每个物品最多五件。因此可以将每个状态,cost,最终状态压缩成五位的六进制数,然后做求最小值的完全背包就行了。当然这题还是用编号的形式给出物品的,你需要把物品弄成连续的编号再做压缩操作。

 #include<cstdio>
#include<iostream>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
#define clrmax(x) memset(x,0x3f3f3f3f,sizeof(x))
#define MAXN 1000010
using namespace std;
int dp[MAXN];
int v[MAXN],c[MAXN];
int inf[MAXN];
int bit(int n)
{
int l=;
n--;
while(n--)
l*=;
return l;
}
bool infer(int i,int j)
{
while(j)
{
if(i%<j%)
return false;
i/=;
j/=;
}
return true;
}
int main()
{
int n,m,maxn,ans,t,k,l,r,cnt,flag,num;
while(scanf("%d",&n)!=EOF)
{
m=;
clr(inf);
clrmax(dp);
for(int i=;i<=n;i++)
{
scanf("%d",&k);
inf[k]=i;
c[i]=bit(i);
scanf("%d%d",&num,&v[i]);
m+=bit(i)*num;
}
cnt=n;
scanf("%d",&k);
for(int i=;i<=k;i++)
{
scanf("%d",&l);
flag=;
r=;
for(int i=;i<=l;i++)
{
scanf("%d%d",&n,&t);
if(inf[n]== || flag==)
{
flag=;
continue;
}
r+=bit(inf[n])*t;
}
if(flag==)
{
c[++cnt]=r;
scanf("%d",&v[cnt]);
}
else
{
scanf("%d",&r);
}
}
dp[]=;
for(int i=;i<=cnt;i++)
{
for(int j=c[i];j<=m;j++)
if(infer(j,c[i]))
{
dp[j]=min(dp[j],dp[j-c[i]]+v[i]);
}
}
printf("%d\n",dp[m]);
}
return ;
}

状压完全背包

hdu 3092 Least common multiple 转化为01背包

把一个数n进行划分,求最大的lcm,并将n%mod。

我们先不要在意%mod。对于一个数n,我们发觉(只有质数或者相同质数的积和剩下一堆1)的划分的情况下n的lcm会达到最大——因为,在第p次非质数的划分你可以拆解成n个质数相乘,将n个质数或者相同质数的乘积唯一化为k个作为划分后,这k个数的和小于等于该非质数,显然后者能使第p次划分完之后剩下的划分数字更大,得出来的lcm更大。当然剩下的多余的数全部拆解成1,这样对答案是不影响的(没有贡献的)。你可以发觉这很像一个01背包,而且是最大容量为n的背包,而物品则是质数或相同质数的积,cost为该数,value也为该数,那么对于每个状态的转移为:dp[j]=max(dp[j-c[i]]*v[i],dp[j])。并且每个物品只影响一次,即为01背包,毕竟一个质数对该状态的贡献只有乘一次该质数而不能多次。所以加上一些n个相同质数的积的细节处理(想想为什么)后做个01背包,即能求解出答案 dp[n]。
 然而相乘数字太大,并且需要mod,我们可以用一个数组dp来存进行特殊处理过的大小以方便进行动态规划,另一个数组ans来存答案,包括%mod。将dp中的数全部log10以表示更大范围,每次转移就变为dp[j]=max(dp[j-c[i]]+log10(v[i]),dp[j])便于比较大小,dp[j]被更新后ans也同样更新,并且%mod。

最后求出来ans[n]即为答案。

 #include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
#define MAXN 10010
using namespace std;
double dp[MAXN];
int ans[MAXN];
int prime[MAXN],inf[MAXN];
int n,m,s,k,t,mod,cnt;
double u,v,b,x;
void primer(int n)
{
clr(inf);
cnt=;
for(int i=;i<=n;i++)
{
if(!inf[i])
{
prime[++cnt]=i;
inf[i]=;
}
for(int j=;j<=cnt;j++)
{
if(prime[j]*i>n) break;
inf[prime[j]*i]=;
if(i%prime[j]==)
break;
}
}
return ;
}
int main()
{
primer();
while(scanf("%d%d",&n,&mod)==)
{
clr(dp);
for(int i=;i<=n;i++)
ans[i]=;
for(int i=;i<=cnt && prime[i]<=n;i++)
{
u=log10(prime[i]*1.0);
for(int j=n;j>=prime[i];j--)
{
for(int k=,t=prime[i];t<=j;k++,t=t*prime[i])
if(dp[j-t]+k*u>dp[j])
{
dp[j]=dp[j-t]+k*u;
ans[j]=(ans[j-t]*t)%mod;
}
}
}
printf("%d\n",ans[n]);
}
return ;
}

背包

poj 1015 Jury Compromise 区间右移+输出路径

这一题,我们需要得出最小的$ |D[sum]-P[sum]|$的情况下最大的$ |D[sum]+P[sum]|$。

那我们可以考虑做一个二维的01背包,其中背包的两个费用为人数和当前做到第i个人的d[sum]-p[sum]。因为这样会涉及负数,因此将整个区间右移。考虑到$ m\le 20$ 以及 $ -20 \le d[i]-d[j] \le 20 $ 因此区间为[-400,400],将其右移为[0.800]。然后我们要求的是背包在当前费用下最大的d[sum]+p[sum]。在按照d[sum]-p[sum]这个费用循环时注意,背包上限是(400<400+c[i])?400:400+c[i]下限是c[i]-400>-400?c[i]-400:-400。

背包处理完以后,在400附近找最靠近400的i,即最小的|d[sum]-p[sum]|,并且 dp[n][m][400+i]是最大的,然后输出(i+dp[n][m][400+i])/2为P[sum],(i-dp[n][m][400+i])/2为D[sum]。

接下来就是按路径输出的问题了,这个路径背包九讲里有讲过怎么获得我就不再赘言了。但是poj说好的是special judge,然后发觉应该输出最大字典序才行。。坑死了,害我看了好久。

 #include<cstdio>
#include<iostream>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
#define clrmin(x) memset(x,-0x3f3f3f3f,sizeof(x))
using namespace std;
int dp[][][];
int c[],v[],ans[];
int n,m,k,s,t,l,r;
int max(int a,int b)
{
return a>b?a:b;
}
int main()
{
int kase=,fit;
while(scanf("%d%d",&n,&m)!=EOF && n> && m>)
{
clrmin(dp);
dp[][][]=;
for(int i=;i<=n;i++)
{
scanf("%d%d",&l,&r);
c[i]=l-r;
v[i]=l+r;
dp[i][][]=;
}
for(int i=;i<=n;i++)
{
for(int j=m;j>;j--)
{
for(int k=(<+c[i])?:+c[i];k>=c[i]- && k>=-;k--)
{
dp[i][j][k+]=max(dp[i-][j][k+],dp[i-][j-][k-c[i]+]+v[i]);
}
}
/* for(int ii=0;ii<=2;ii++)
{
for(int jj=398;jj<=406;jj++)
printf("%d ",dp[i][ii][jj]>=0?dp[i][ii][jj]:-1);
printf("\n");
} */
// printf("%d\n",dp[i][0][400]);
}
for(int i=;i<=;i++)
{
if(dp[n][m][+i]> || dp[n][m][-i]>)
{
if(dp[n][m][+i]>dp[n][m][-i])
l=i;
else
l=-i;
break;
}
}
printf("Jury #%d\n",++kase);
printf("Best jury has value %d for prosecution and value %d for defence:\n",(l+dp[n][m][+l])/,(dp[n][m][+l]-l)/);
l=+l;
t=m;
for(int i=n;i>=;i--)
if(dp[i][m][l]==dp[i-][m-][l-c[i]]+v[i])
{
ans[m]=i;
m--;
l-=c[i];
if(m==)
break;
}
for(int i=;i<=t;i++)
printf(" %d",ans[i]);
printf("\n\n");
}
return ;
}

二维01背包

做了这么些题,可以发觉背包题需要对费用很敏感,对于是精确到某个容量下的(即装满)的背包和非精确(即最多为该容量)的背包,赋初值的方式是不同的。还有要善于发掘背包的费用,费用可以是任何可以用来递推的下标。可能是多维(2以上)要擅用状态压缩使得问题简单化。

当然对于dp都有一个共同点:数据范围特别小。注意下数据范围有可能就能发现他是一道dp甚至背包,从而快速解决。

背包系列练习及总结(hud 2602 && hdu 2844 Coins && hdu 2159 && poj 1170 Shopping Offers && hdu 3092 Least common multiple && poj 1015 Jury Compromise)的更多相关文章

  1. POJ 1170 Shopping Offers(完全背包+哈希)

    http://poj.org/problem?id=1170 题意:有n种花的数量和价格,以及m种套餐买法(套餐会便宜些),问最少要花多少钱. 思路:题目是完全背包,但这道题目不好处理的是套餐的状态, ...

  2. HDU 3092 Least common multiple 01背包

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3092 Least common multiple Time Limit: 2000/1000 MS ...

  3. HDOJ(HDU).2844 Coins (DP 多重背包+二进制优化)

    HDOJ(HDU).2844 Coins (DP 多重背包+二进制优化) 题意分析 先把每种硬币按照二进制拆分好,然后做01背包即可.需要注意的是本题只需要求解可以凑出几种金钱的价格,而不需要输出种数 ...

  4. HDU 1170 Shopping Offers 离散+状态压缩+完全背包

    题目链接: http://poj.org/problem?id=1170 Shopping Offers Time Limit: 1000MSMemory Limit: 10000K 问题描述 In ...

  5. poj 1015 Jury Compromise(背包+方案输出)

    \(Jury Compromise\) \(solution:\) 这道题很有意思,它的状态设得很...奇怪.但是它的数据范围实在是太暴露了.虽然当时还是想了好久好久,出题人设了几个限制(首先要两个的 ...

  6. hdu 2844 Coins (多重背包)

    题意是给你几个数,再给你这几个数的可以用的个数,然后随机找几个数来累加, 让我算可以累加得到的数的种数! 解题思路:先将背包初始化为-1,再用多重背包计算,最后检索,若bb[i]==i,则说明i这个数 ...

  7. hdu 2844 Coins (多重背包+二进制优化)

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=2844 思路:多重背包 , dp[i] ,容量为i的背包最多能凑到多少容量,如果dp[i] = i,那么代表 ...

  8. HDU 2844 Coins (多重背包计数 空间换时间)

    Coins Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Subm ...

  9. HDU 2844 Coins(多重背包)

    点我看题目 题意 :Whuacmers有n种硬币,分别是面值为A1,A2,.....,An,每一种面值的硬币的数量分别是C1,C2,......,Cn,Whuacmers想买钱包,但是想给人家刚好的钱 ...

随机推荐

  1. 12.24笔记(关于//UIDynamic演练//多对象的附加行为//UIDynamic简单演练//UIDynamic//(CoreText框架)NSAttributedString)

          12.24笔记1.UIDynamic注意点:演示代码:上面中设置视图旋转的时候,需要注意设置M_PI_4时,视图两边保持平衡状态,达不到仿真效果.需要偏移下角度.2.吸附行为3.推动行为初 ...

  2. VMware 12安装虚拟机Mac OS X 10.10使用小技巧(虚拟机Mac OS X 10.10时间设置,虚拟机Mac OS X 10.10通过代理上网,Mac OS X 10.10虚拟机优化,VMware虚拟机相互复制)

    1:修改Mac OS 系统时间 2:Mac OS系统 通过代理上网 VMware 12安装Mac OS X 10.10虚拟机优化心得 虚拟显卡硬伤,所以必须要优化下才能用,优化的原则就是能精简的精简, ...

  3. bzoj 1968 数学

    在1-n所有数中,i一共可以成为n/i个数的约数,也即所有的约数中有n/i个i,所以扫一遍累加答案就好了. /********************************************* ...

  4. vs调试 配置IISExpress允许局域网内部访问

    内网可访问后,本机不能使用localhost   1.找到IISExpress的配置文件,位于 <文档>/IISExpress/config文件夹下,打开applicationhost.c ...

  5. A题 hdu 1235 统计同成绩学生人数

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1235 统计同成绩学生人数 Time Limit: 2000/1000 MS (Java/Others) ...

  6. MHA切换过程:

    1.监测master的状态Ping(SELECT) succeeded, waiting until MySQL doesn't respond.. 2.当监控发现master异常时发出warning ...

  7. 110.Balanced Binary Tree---《剑指offer》面试39

    题目链接 题目大意:判断一个二叉树是否是平衡二叉树. 法一:dfs.利用求解二叉树的高度延伸,先计算左子树的高度,再计算右子树的高度,然后两者进行比较.o(nlgn).代码如下(耗时4ms): pub ...

  8. c++设计模式系列----builder模式

    看了好几处关于builder模式的书和博客,总感觉不是很清楚,感觉不少书上的说的也不是很准确.最后还是看回圣经<设计模式>.看了好久终于感觉明白了一点了. 意图: builder模式提出的 ...

  9. PBFT算法的相关问题

    PBFT(99.02年发了两篇论文)-从开始的口头算法(指数级)到多项式级 要求 n>3f why: 个人简单理解:注意主节点是可以拜占庭的,从节点对于(n,v,m)的投票最开始也是基于主节点给 ...

  10. CentOS下配置FTP

    http://www.cnblogs.com/zhenmingliu/archive/2012/04/25/2470646.html 常见错误: 1.FTP服务器已经拒绝 解决方案 # setenfo ...