背包代码放一放。
Big Event in HDU hdu1171
题目意思: 给很多组数组,平方他们,使他们的相差尽可能小。
#include<stdio.h>
int f[250001],a[51],b[51];
int main()
{
int i,j,k,n,t,s,temp;
__int64 m;
while(scanf("%d",&t)>0)
{
if(t<0)break;
for(i=1,m=0;i<=t;i++)
{
scanf("%d%d",&a[i],&b[i]);
m=m+a[i]*b[i];
}
k=m/2;
for(i=1;i<=k;i++)
f[i]=0;
for(i=1;i<=t;i++)
for(s=1;s<=b[i];s++)
for(j=k;j>=a[i];j--)
{
temp=f[j-a[i]]+a[i];
if(temp>f[j])
f[j]=temp;
}
i=f[k];j=m-f[k];
if(j>i) printf("%d %d\n",j,i);
else printf("%d %d\n",i,j);
}
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Bone Collector hdu2602
题意:简单的01背包
#include<stdio.h>
__int64 a[1001],b[1001],f[1001]; //2^31哦,错了两次
int main()
{
__int64 i,j,k,n,m,t,v,temp;
while(scanf("%I64d",&m)>0)
{
while(m--)
{
scanf("%I64d%I64d",&n,&v);
for(i=1;i<=n;i++)
scanf("%I64d",&a[i]);
for(i=1;i<=n;i++)
scanf("%I64d",&b[i]);
for(i=0;i<=v;i++)
f[i]=0;
for(i=1;i<=n;i++)
for(j=v;j>=b[i];j--)
{
temp=f[j-b[i]]+a[i];
if(temp>f[j])
f[j]=temp;
}
printf("%I64d\n",f[v]);
}
}
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Piggy-Bank hdu 1114
题目意思:告诉你罐子的重量,算能装最小的钱数
完全背包,而且装满。
#include<stdio.h>
__int64 a[501],b[501],f[10001];
int main()
{
__int64 i,j,k,n,m,t,e,temp;
while(scanf("%I64d",&t)>0)
{
while(t--)
{
scanf("%I64d%I64d",&e,&m);
k=m-e;
scanf("%I64d",&n);
for(i=1;i<=k;i++)
f[i]=100000000;
f[0]=0;
for(i=1;i<=n;i++)
scanf("%I64d%I64d",&a[i],&b[i]);
for(i=1;i<=n;i++)
for(j=b[i];j<=k;j++)
{
temp=f[j-b[i]]+a[i];
if(temp<f[j]) //核心思想。虽然短,但是初值设为最大是有好处的。
f[j]=temp;
}
// for(i=1;i<=k;i++)
// printf("%I64d ",f[i]);
if(f[k]==100000000) printf("This is impossible.\n");
else
printf("The minimum amount of money in the piggy-bank is %I64d.\n",f[k]);
}
}
return 0;
}
做这一题的时候,刚开始也是设定一个最大值,然后改成初值设为-1,如果把初值设为-1,那么要怎么样改呢?
用初始值为负值过了。
#include<stdio.h>
__int64 a[501],b[501],f[10001];
int main()
{
__int64 i,j,k,n,m,t,e,temp;
while(scanf("%I64d",&t)>0)
{
while(t--)
{
scanf("%I64d%I64d",&e,&m);
k=m-e;
scanf("%I64d",&n);
for(i=1;i<=k;i++)
f[i]=-100000000;
f[0]=0;
for(i=1;i<=n;i++)
scanf("%I64d%I64d",&a[i],&b[i]);
for(i=1;i<=n;i++)
for(j=b[i];j<=k;j++)
{
temp=f[j-b[i]]+a[i];
if(j-b[i]==0&&f[j]==-100000000) //空的状态的时,直接填入。
f[j]=temp;
else if(f[j]==-100000000&&f[j-b[i]]!=-100000000)//另一种为空的情况时候的填入。
f[j]=temp;
else if(f[j-b[i]]!=-100000000&&temp<f[j]) //不是为空,但是数值更小的时候,替换掉
f[j]=temp;
}
// for(i=1;i<=k;i++)
// printf("%I64d ",f[i]);
if(f[k]==-100000000) printf("This is impossible.\n");
else
printf("The minimum amount of money in the piggy-bank is %I64d.\n",f[k]);
}
}
return 0;
}
刚开始做这一题的时候,少了个else if(f[j-b[i]]!=-100000000&&temp<f[j]),前半部分。f[j-b[i]]!=-100000000
下面书写更加简单。
#include<stdio.h>
__int64 a[501],b[501],f[10001];
int main()
{
__int64 i,j,k,n,m,t,e,temp;
while(scanf("%I64d",&t)>0)
{
while(t--)
{
scanf("%I64d%I64d",&e,&m);
k=m-e;
scanf("%I64d",&n);
for(i=1;i<=k;i++)
f[i]=-1;
f[0]=0;
for(i=1;i<=n;i++)
scanf("%I64d%I64d",&a[i],&b[i]);
for(i=1;i<=n;i++)
for(j=b[i];j<=k;j++)
{
temp=f[j-b[i]]+a[i];
if((j-b[i]==0&&f[j]==-1)||(f[j]==-1&&f[j-b[i]]!=-1)||(temp<f[j]&&f[j-b[i]]!=-1))
f[j]=temp;
}
// for(i=1;i<=k;i++)
// printf("%I64d ",f[i]);
if(f[k]==-1) printf("This is impossible.\n");
else
printf("The minimum amount of money in the piggy-bank is %I64d.\n",f[k]);
}
}
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
饭卡 hdu2546
题目意思:01背包。但是有个注意的问题。
#include<stdio.h>
__int64 f[1001],a[1001];
void pd(__int64 length)
{
__int64 i,j,k,temp;
for(i=1;i<=length;i++)
{
k=i;
for(j=i;j<=length;j++)
if(a[j]<a[k]) k=j;
temp=a[k];
a[k]=a[i];
a[i]=temp;
}
}
int main()
{
__int64 i,j,n,m,temp,flag;
while(scanf("%I64d",&n)>0&&n)
{
for(i=1;i<=n;i++)
scanf("%I64d",&a[i]);
scanf("%I64d",&m);
pd(n);
// for(i=1;i<=n;i++)
// printf("%I64d ",a[i]);
flag=0;
if(m<5){
printf("%I64d\n",m);
continue;
}
else if(m==5){
printf("%I64d\n",m-a[n]);
continue;
}
if(a[n]>5){flag=1; m=m-5;}
else n++;
for(i=1;i<=m;i++)
f[i]=0;
for(i=1;i<n;i++)
for(j=m;j>=a[i];j--)
{
temp=f[j-a[i]]+a[i];
if(temp>f[j])
f[j]=temp;
}
if(flag==0)
printf("%I64d\n",m-f[m]);
else printf("%I64d\n",m-f[m]+5-a[n]);
}
return 0;
}
做这题的时候,居然把排序 写错,无语中。。。。。。。前次用筛选法素数也是。怎么办???
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I NEED A OFFER! hdu1203
题目意思:某吊死要出国考试,给你进入的几率和钱数,让你算,只要能让他出国的最大概率。
1-(都不能出国的概率)=至少一份offer的最大概率
#include<stdio.h>
int a[10001];
float b[10001],f[10001];
int main()
{
int i,j,k,m,n,t;
float temp;
while(scanf("%d%d",&n,&m)>0)
{
if(n==0&&m==0)break;
for(i=1;i<=m;i++)
{
scanf("%d%f",&a[i],&b[i]);
b[i]=1-b[i];
}
for(i=0;i<=n;i++)
f[i]=1.0;
for(i=1;i<=m;i++)
for(j=n;j>=a[i];j--)
{
temp=f[j-a[i]]*b[i];
if(temp<f[j])
f[j]=temp;
}
printf("%.1f%%\n",(1-f[n])*100);
}
return 0;
}
1A过的。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Dividing 多重背包 hdu1059
题意:平均分成两个等价值。
#include<stdio.h>
int a[7],f[120002];
void beibao(int n,int i)
{
int j,temp;
for(j=n;j>=i;j--)
{
temp=f[j-i]+i;
if(temp>f[j])
f[j]=temp;
}
}
void wqbeibao(int n,int i)
{
int j,temp;
for(j=i;j<=n;j++)
{
temp=f[j-i]+i;
if(temp>f[j])
f[j]=temp;
}
}
int main()
{
int i,j,k,n,t,time=0;
while(1)
{
for(i=1,k=0;i<=6;i++)
{
scanf("%d",&a[i]);
k=k+a[i]*i;
}
for(i=0,j=1;j<=6;j++)
if(a[j]==0) i++;
if(i==6) break;
printf("Collection #%d:\n",++time);
if(k%2==1){
printf("Can't be divided.\n\n");
continue;
}
for(i=0;i<=k;i++)
f[i]=0;
n=k/2;
for(i=1;i<=6;i++)
{
if(a[i]*i>=n)
wqbeibao(n,i);
else //二进制优化时从1开始。不是从2.
{
t=1;
while(t<a[i])
{
beibao(n,t*i);
a[i]=a[i]-t;
t=t*2;
}
beibao(n,a[i]*i);
}
}
// for(i=1;i<=n;i++)
// printf("%d ",f[i]);
if(f[n]==n) printf("Can be divided.\n");
else printf("Can't be divided.\n");
printf("\n");
}
return 0;
}
刚开始写错了2点,1,优化从2开始错了。2,少了\n在奇数时。
这个用的是没有装满的,但是由于f[j-i]+i;所以可以用来判断是否装满。当然用常规的 装满的方法也是可以的,测试时时间花了更多。?代码
#include<stdio.h>
int a[7],f[120002];
void beibao(int n,int i)
{
int j,temp;
for(j=n;j>=i;j--)
{
temp=f[j-i]+i;
if(j-i==0&&f[j]==-1)
f[j]=temp;
if(f[j-i]!=-1&&f[j]==-1)
f[j]=temp;
if(temp>f[j]&&f[j-i]!=-1)
f[j]=temp;
}
}
void wqbeibao(int n,int i)
{
int j,temp;
for(j=i;j<=n;j++)
{
temp=f[j-i]+i;
if(j-i==0&&f[j]==-1)
f[j]=temp;
if(f[j-i]!=-1&&f[j]==-1)
f[j]=temp;
if(temp>f[j]&&f[j-i]!=-1)
f[j]=temp;
}
}
int main()
{
int i,j,k,n,t,time=0;
while(1)
{
for(i=1,k=0;i<=6;i++)
{
scanf("%d",&a[i]);
k=k+a[i]*i;
}
for(i=0,j=1;j<=6;j++)
if(a[j]==0) i++;
if(i==6) break;
printf("Collection #%d:\n",++time);
if(k%2==1){
printf("Can't be divided.\n\n");
continue;
}
n=k;
for(i=1;i<=n;i++)
f[i]=-1;
f[0]=0;
n=n/2;
for(i=1;i<=6;i++)
{
if(a[i]*i>n)
wqbeibao(n,i);
else
{
t=1;
while(t<a[i])
{
beibao(n,t*i);
a[i]=a[i]-t;
t=t*2;
}
beibao(n,a[i]*i);
}
}
// for(i=1;i<=n;i++)
// printf("%d ",f[i]);
if(f[n]==n) printf("Can be divided.\n");
else printf("Can't be divided.\n");
printf("\n");
}
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Coins hdu 2844 poj 1742
题意:告诉你n种价值的硬币和个数,让你求1-m种刚好能凑出的个数。
多重背包问题
#include<stdio.h>
int a[101],b[101],f[100002];
void bb01(int v,int jz)
{
int i,temp;
for(i=v;i>=jz;i--)
{
temp=f[i-jz]+jz;
if(temp>f[i])
f[i]=temp;
}
}
void bbwq(int v,int jz)
{
int i,temp;
for(i=jz;i<=v;i++)
{
temp=f[i-jz]+jz;
if(temp>f[i])
f[i]=temp;
}
}
int main()
{
int i,j,k,n,m;
while(scanf("%d%d",&n,&m)>0)
{
if(n==0&&m==0)break;
for(i=1;i<=n;i++)
scanf("%d",&a[i]);//jia zhi
for(i=1;i<=n;i++)
scanf("%d",&b[i]); //ge shu
for(i=1;i<=m;i++)
f[i]=0;
for(i=1;i<=n;i++)
{
if(a[i]*b[i]>=m)
bbwq(m,a[i]);
else
{
k=1;
while(k<b[i])
{
bb01(m,k*a[i]);
b[i]=b[i]-k;
k=k*2;
}
bb01(m,a[i]*b[i]);
}
}
for(i=1,j=0;i<=m;i++)
if(i==f[i]) j++;
printf("%d\n",j);
}
return 0;
}
这一题由于上一题的启发,没有用装满来做,因为f[i-zj]+jz,所以只有判断i==f[i]??就可以了。
1A题。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
悼念512汶川大地震遇难同胞——珍惜现在,感恩生活 hdu2191
题意:多重背包,不要二进制优化都能过。
#include<stdio.h>
int f[101],a[101],b[101],c[101];
int main()
{
int i,j,k,n,m,temp,t;
while(scanf("%d",&t)>0)
{
while(t--)
{
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
f[i]=0;
for(i=1;i<=m;i++)
scanf("%d%d%d",&a[i],&b[i],&c[i]);
for(i=1;i<=m;i++)
for(k=1;k<=c[i];k++)
for(j=n;j>=a[i];j--)
{
temp=f[j-a[i]]+b[i];
if(temp>f[j])
f[j]=temp;
}
printf("%d\n",f[n]);
}
}
return 0;
}
//1A
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FATA HDU2159(g)
题意:打怪。告诉你需要的经验值,已有的忍耐度和n种怪的情况,并且限定了打怪次数。
二重背包问题
#include<stdio.h>
#include<stdlib.h>
int f[101][101],b[101],a[101];
int main()
{
int i,j,k,n,m,t,s,temp;
while(scanf("%d%d%d%d",&n,&m,&k,&s)>0)
{
for(i=1;i<=k;i++)
scanf("%d%d",&a[i],&b[i]);
for(i=0;i<=m;i++)
for(j=0;j<=s;j++)
f[i][j]=0;
for(i=1;i<=k;i++)
for(j=b[i];j<=m;j++)
for(t=1;t<=s;t++)
{
temp=f[j-b[i]][t-1]+a[i];
if(temp>f[j][t])
f[j][t]=temp;
}
if(f[m][s]<n) printf("-1\n");
else
{
for(i=1;i<=m;i++)
if(f[i][s]>=n) //刚开始 f[s][i]==n错了。
{
printf("%d\n",m-i);
break;
}
}
// for(i=1;i<=m;i++)
// printf("%d ",f[i][s]);
}
return 0;
}
做这题的时候,转化了一下思想。f[t][j]表示的是用 t的忍耐度打j次怪获得的最大经验值。
在《背包九讲》看到,在二维背包中 当物品只是取一次的时候,用逆序;完全背包时用 顺序。
在二维中同样如此。
有一个小问题: 最后两个for循环的位置能否调换????? 是的,可以。
代码中把个数放到了后面。
#include<stdio.h>
#include<stdlib.h>
int f[101][101],b[101],a[101];
int main()
{
int i,j,k,n,m,t,s,temp;
while(scanf("%d%d%d%d",&n,&m,&k,&s)>0)
{
for(i=1;i<=k;i++)
scanf("%d%d",&a[i],&b[i]);
for(i=0;i<=s;i++)
for(j=0;j<=m;j++)
f[i][j]=0;
for(i=1;i<=k;i++)
for(t=1;t<=s;t++) //与上一次的做法是,把两个for倒置了一下。
for(j=b[i];j<=m;j++)
{
temp=f[t-1][j-b[i]]+a[i];
if(temp>f[t][j])
f[t][j]=temp;
// printf("%d ",temp);
}
if(f[s][m]<n) printf("-1\n");
else
{
// system("pause");
for(i=1;i<=m;i++)
if(f[s][i]>=n) //刚开始 f[s][i]==n错了。
{
printf("%d\n",m-i);
break;
}
}
// for(i=1;i<=m;i++)
// printf("%d ",f[i][s]);
}
return 0;
}
你觉得用二维做是常规思维,但是,我发现自己以前做这一题的代码时候,自己也愣住了。碉堡了,一维数组过的,开另一个数组去记录个数。
#include<stdio.h>
#include<stdlib.h>
int f[101],a[101],b[101],e[101];
int main()
{
int i,j,k,n,m,s,temp;
while(scanf("%d%d%d%d",&n,&m,&k,&s)>0)
{
for(i=0;i<=m;i++)
f[i]=e[i]=0;
for(i=0;i<k;i++)
scanf("%d%d",&a[i],&b[i]);
for(i=0;i<k;i++)
for(j=b[i];j<=m;j++)
{
temp=f[j-b[i]]+a[i];
if(temp>f[j]&&e[j-b[i]]<s) //核心思想。
{
f[j]=temp;
e[j]=e[j-b[i]]+1;
}
}
if(f[m]>=n)
{
for(i=0;i<=m;i++)
if(f[i]>=n){printf("%d\n",m-i);break;}//注意啊,f[i]>=n不是f[i]==n
}
else printf("-1\n");
}
return 0;
}
看看后面的 (注意啊)呵呵。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ACboy needs your help hdu1712 ........
分组背包问题,刚开始没有理解好。错了,犯了和背包九讲里一样的错误,最后两个for的调换
每组最多选一件,也就是说,一门选了一次就可以了。
刚开始不明白这个二维数组的设置是什么用意,发现,这个二维数组,正好用来表示分组背包的问题啊。
以后用来给你数据存取的时候也就用得到了。
#include<stdio.h>
__int64 f[101],a[101][101];
int main()
{
__int64 i,j,k,n,m,t,temp,s;
while(scanf("%I64d%I64d",&n,&m)>0)
{
if(n==0&&m==0)break;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%I64d",&a[i][j]);
for(i=0;i<=m;i++)
f[i]=0;
for(i=1;i<=n;i++)
for(j=m;j>=1;j--)
for(s=m;s>=1;s--)
{
if(j-s<0) continue;
temp=f[j-s]+a[i][s];
if(temp>f[j])
f[j]=temp;
}
printf("%I64d\n",f[m]);
}
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
hdu 2693 Bone Collector II
题意:背包的次优解,第k大数。
自己水,没有思路,也看不懂大神的背包分析。看了别人代码也想了好久,最后知道原理。了。
为什么这个方法正确呢?实际上,一个正确的状态转移方程的求解过程遍历了所有可用的策略,也就覆盖了问题的所有方案。只不过由于是求最优解,所以其 它在任何一个策略上达不到最优的方案都被忽略了。如果把每个状态表示成一个大小为K的数组,并在这个数组中有序的保存该状态可取到的前K个最优值。那么, 对于任两个状态的max运算等价于两个由大到小的有序队列的合并。另外还要注意题目对于“第K优解”的定义,将策略不同但权值相同的两个方案是看作同一个解还是不同的解。如果是前者,则维护有序队列时要保证队列里的数没有重复的。
用个形象的比喻吧:如果我想知道学年最高分,那么,我只要知道每个班级的最高分,然后统计一遍就可以了。如果我想知道学年前十呢?我必须要知道每个班的前十名。大家在心里模拟一下,对,这就是本题核心的算法。两种决策,就可以看作这个学年只有两个班。
#include<stdio.h>
int f[1001][31],jz[101],zl[101],a[31],b[30];
int main()
{
int i,j,k,n,time,temp,m,v;
int x,y,z;
while(scanf("%d",&time)>0)
{
while(time--)
{
scanf("%d%d%d",&n,&v,&k);
for(i=1;i<=n;i++)
scanf("%d",&jz[i]);
for(i=1;i<=n;i++)
scanf("%d",&zl[i]);
for(i=0;i<=v;i++)
for(j=0;j<=k;j++)
f[i][j]=0;
for(i=1;i<=n;i++)
for(j=v;j>=zl[i];j--)
{
for(m=1;m<=k;m++)
{
a[m]=f[j-zl[i]][m]+jz[i];
b[m]=f[j][m];
} //这个可以很好理解。
x=y=z=1;
a[m]=-1;
b[m]=-1;
while(z<=k&&(x<=k||y<=k)) //原先的想法错误了。
{
if(a[x]>=b[y])
{
f[j][z]=a[x];
x++;
}
else
{
f[j][z]=b[y];
y++;
}
if(f[j][z]!=f[j][z-1]) //思考思考。
z++;
}//在a b数组中找出k个不同的数字,不同的哦~没有的话,就 //去前几个。
}
printf("%d\n",f[v][k]);
}
}
return 0;
}
在a b数组中找出k个不同的数字,不同的哦~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~