总结之前:先感谢一下http://blog.csdn.net/yexiaohhjk/article/details/50229489
博主的所提供的题目及题解。
其状态转移方程是:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
题目给出T(示例个数):N(骨头个数)V(背包大小)
val{1,2,3,4,5}//对应编号骨头的价值。
w{5,4,3,2,1,}//对应编号骨头的容量。
表格第一行和第一列是故意空出来的。
以编号为行标,以容量为纵标。所以才有 6*11的表格,
根据 f[i][v] = max { f[i-1][v] , f [i-1][v-c[i]] +w[i] };
制作表格。答案就是最后的dp[N][V];
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 1
0 0 0 0 2 2 2 2 2 3 3
0 0 0 3 3 3 3 5 5 5 5
0 0 4 4 4 7 7 7 7 9 9
0 5 5 9 9 9 12 12 12 12 14
代码实现:
#include
#include
#include
typedef long long ll;
using namespace std;
int main()
{
int T;
cin>>T;
while(T--){
int N,V;
cin>>N>>V;
int w[1001],val[1001];
int dp[1001][1001];
memset(dp,0,sizeof(dp));
for(int i=1;i<=N;i++){
cin>>val[i];
}for(int i=1;i<=N;i++){
cin>>w[i];
}for(int i=1;i<=N;i++){
for(int j=0;j<=V;j++){
if(j>=w[i]){
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+val[i]);
}else{
dp[i][j]=dp[i-1][j];
}
}
}
/*for(int i=0;i<=N;i++){
for(int j=0;j<=V;j++){
printf("%-3d",dp[i][j]);
}printf("\n");
}*/
printf("%d\n",dp[N][V]);
}return 0;
}
其实还有一些更省空间和时间的方法。
一维数组处理数据
for(int i=0;i
for(int j=V;j>=weight[i];j--)
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
因为大家可以想象一下,其实二维也是根据上一列的 dp[i-1][j-weight[i]] 处理的
浪费空间,所以可以压缩成一维处理,处理时也是和二维的一样,不过是直接根据dp[j-weight[i]]比较
注意一点的是,里面的for循环,是从V开始的,目的是保证 在能放的前提下,放东西,而且为什么从后往前
而不是从for j=weight[i] ~V;
这是因为物品只能放一遍,决策时有一个念头就是快满了(比如剩下一个物品),就能放就放,不必考虑最优的情况
参考代码:
#include
#include
#include
#include
using namespace std;
int main()
{
int T;
cin>>T;
while(T--){
int N,V,w[1001],dp[1001],val[1001];
memset(dp,0,sizeof(dp));
cin>>N>>V;
for(int i=1;i<=N;i++){
cin>>val[i];
}for(int i=1;i<=N;i++){
cin>>w[i];
}for(int i=1;i<=N;i++){
for(int j=V;j>=w[i];j--){
dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
}
}printf("%d\n",dp[V]);
}
return 0;
}
HDU-2546-饭卡(01背包+一点细节)
其实这题也是裸的01背包题。但是加了一点细节,就是加了一个5块的特权,
贪心的想法出发,要是我能用最后的5块买任何东西为什么不买最贵的东西呢,
一般的花费是 m,m-a[i]........8、7、6、5、4、3、2、1、0.
解题方法 是 m、m-1、、、、、、、6 五块单独拿出来
用m-5的容量装东西,剩下5块买最贵的物品
详细代码:
#include
#include
#include
#include
using namespace std;
int main()
{
int n;
while(cin>>n&&n){
int a[1050],m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
cin>>m;
if(m<5){
printf("%d\n",m);
continue;
}
int dp[1050];
m-=5;
sort(a+1,a+n+1);
memset(dp,0,sizeof(dp));
for(int i=1;i=a[i];j--){
dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
}
}
cout<
Big Event in HDU(HDU-1171)
http://acm.hdu.edu.cn/showproblem.php?pid=1171
这题的大意是,尽量分成两份,这题是01背包的变形
以sum/2为容量,让它尽量放满。
a-代表的是价值,b-是数量。我们处理这问题还需要考虑离散化。
#include
#include
#include
#include
using namespace std;
int main()
{
int n;
while(cin>>n){
if(n<=0)return 0;
int dp[300000]={0};
int cnt=0,p[5050],sum=0;
while(n--){
int a,b;
cin>>a>>b;
while(b--){
p[cnt++]=a;
sum+=a;
}
}
for(int i=0;i=p[i];j--){
dp[j]=max(dp[j],dp[j-p[i]]+p[i]);
}
}
printf("%d %d\n",max(sum-dp[sum/2],dp[sum/2]),min(sum-dp[sum/2],dp[sum/2]));
}
return 0;
}
Bone Collector II
http://acm.hdu.edu.cn/showproblem.php?pid=2639
这题是找第K大的背包,大家思考一下,
就如要找年级前十名,我需要的是各个班里前十名的成绩,
目前就是两个班,我想要的是第K名,我需要的是两个K 。
有两个代码 ,第一是discuss里面的,第二个是别人的博客上的。
#include
using namespace std;
const int maxn=1006;
int dp[maxn][maxn];
int w[maxn],val[maxn],v[maxn],cnt;
int n,W,K;
bool cmp(int x,int y)
{
return x>y;
}
void solve()
{
for(int i=0;i=w[i];j--){
cnt=0;
for(int th=1;th<=K;th++){
val[cnt++]=dp[j][th];
val[cnt++]=dp[j-w[i]][th]+v[i];
}sort(val,val+cnt,cmp);
cnt=unique(val,val+cnt)-val;
for(int th=1;th<=min(K,cnt);th++){
dp[j][th]=val[th-1];
}
}
}
}
int main()
{
int T;
cin>>T;
while(T--){
cin>>n>>W>>K;
memset(dp,0,sizeof(dp));
for(int i=0;i>v[i];
for(int i=0;i>w[i];
solve();
cout<
#include
using namespace std;
int dp[1010][50],A[50],B[50],value[110],cost[110];
int main()
{
int T;
cin>>T;
while(T--){
int n,k,v;
cin>>n>>v>>k;
memset(dp,0,sizeof(dp));
for(int i=0;i>value[i];
for(int i=0;i>cost[i];
for(int i=0;i=cost[i];j--){
for(int kk=0;kkB[b])dp[j][c]=A[a],a++;
else dp[j][c]=B[b],b++;
if(dp[j][c]!=dp[j][c-1]) c++;
}
}
}
cout<
Robberies
http://acm.hdu.edu.cn/showproblem.php?pid=2955
输入的变量有点多,
T
P、N
m1 p1
....
m(n) ,p(n).
P指的是总的被抓概率,而最后的答案一定是比它要小的。
Pi-是偷每一个的银行,被抓的概率。
这题是一个思维的转变。首先是打破我们以往的背包容量的概念,
要是不告诉你概率的精度可能是0.00000001的话,你可能就把概率当成容量进行01背包。
无可奈何就是需要,把钱当作容量,要是dp[m]=1-p ; m是钱,而1-p是逃跑概率。
大家可以设想一下,要是dp[0]=1,逃跑概率达到100%;
只有逃跑率才会等于各个逃跑率之积,被抓的概率不会等于各个被抓的概率之积
最后dp[i]是拿i钱的逃跑率,dp[ans]>P。for(int i=sum~0)一旦找到逃跑率大的就输出
虽然说比较复杂但是,多想还是很有意思的。
#include
using namespace std;
int main()
{
int T;
cin>>T;
while(T--){
double p[110],P,dp[10010];
memset(dp,0,sizeof(dp));
int sum=0,v[110],n;
cin>>P>>n;
P=1.0-P;
for(int i=0;i>v[i]>>p[i];
p[i]=1.0-p[i];
sum+=v[i];
}
dp[0]=1.0;
for(int i=0;i=0;j--){
dp[j]=max(dp[j],dp[j-v[i]]*p[i]);
}
}
for(int i=sum;i>=0;i--){
if(dp[i]-P>0.000000001){
cout<
HDU_3466 Proud Merchants
http://acm.hdu.edu.cn/showproblem.php?pid=3466
N、M
有N行,M是自己拥有的金币。
然后有N行数据: Pi, Qi and Vi
加了排序的01背包。
#include
using namespace std;
typedef struct point{
int p,Q,v,m;
}point;
int cmp(point a,point b)
{
return a.Q-a.p=a[i].Q;j--){
dp[j]=max(dp[j],dp[j-a[i].p]+a[i].v);
}
}
cout<
HUD-1864发票问题
该题要注意的就是每张单子A种类的总和不能大与600,同样B,C类也一样,
还有注意如果不是A,B,C类的不可以报销;
该题就是要把浮点型变成整数这样才能用01背包,这里就只要乘以100就可以了。
#include
using namespace std;
int dp[3000010];
int p[50];
int main()
{
double q;
int n;
while(~scanf("%lf%d",&q,&n),n)
{
int Q=(int)(q*100),cnt=0;
while(n--)
{
int t,a=0,b=0,c=0,flag=0;
scanf("%d",&t);
while(t--)
{
char ch; double f;
scanf(" %c:%lf",&ch,&f);
switch(ch){
case 'A':a+=f*100;break;
case 'B':b+=f*100;break;
case 'C':c+=f*100;break;
default:flag=1;break;
}
if(a+b+c>100000||a>60000||b>60000||c>60000) flag=1;
}
if(flag) continue;
else p[cnt++]=a+b+c;
}
memset(dp,0,sizeof(dp));
for(int i=0;i=p[i];j--){
dp[j]=max(dp[j],dp[j-p[i]]+p[i]);
}
}
printf("%.2lf\n",dp[Q]/(100.0));
}
}
dream city
题意:有n颗树,每棵树都有固定的初始值a[i],和有增长值b[i],给出m天,每天只能砍一棵树,那么对与第j天,
如果选择砍掉第i棵树,那么收获的硬币就是a[i]+b[i]*(j-1),那么怎样砍才能使收获的硬币最多
思路:此时,我们给出的贪心策略是:增长越快的树越晚砍,为什么?
有人可能觉得如果一棵树的a[i]很大,尽管b[i]很小,放在后面也能得到较高的值,其实这样想是错的。
可以想下,要是我们已经得出了一种最优方案,砍掉了e颗树,那么我们所收获的硬币是不是就是
sum=a[1]+a[2]+b[2]+a[3]+(b[3]*2)+.......+a[e]+(b[e]*(e-10));进行下化简
sum=a[1]+a[2]+...+a[e]+b[2]+(b[3]*2)+....+(b[e]*(e-1)),可以看到,前面那一串a数组加起来的值是固定的,
sum最后的值其实只和b什么时候砍有关,所以必须保证b越大越晚砍
每棵树可以选择砍与不砍,所以这是一个01背包问题,排完序后直接上01背包模板就行,
下面给出记忆化搜搜和递推的写法
#include
#include
#include
#define mp make_pair
using namespace std;
typedef pair pii;
pii a[300];
int n,m,t,u,v;
int dp[300][300];
bool cmp(const pii a,const pii b)
{
return a.second
#include
#include
#include
using namespace std;
typedef pair pii;
pii a[300];
int dp[300][300];
int n,m;
bool cmp(const pii a,const pii b)
{
return a.secondm||cur>n) return 0;
if(dp[cur][day]!=-1) return dp[cur][day];
int &res=dp[cur][day];
res=max(dfs(cur+1,day),dfs(cur+1,day+1)+a[cur].first+a[cur].second*(day-1));
return res;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i].first);
}
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i].second);
}
sort(a+1,a+n+1,cmp);
memset(dp,-1,sizeof(dp));
int ans=dfs(1,1);
printf("%d\n",ans);
}
return 0;
}