背包代码放一放。

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个不同的数字,不同的哦~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

 

posted @ 2013-04-07 12:27  芷水  阅读(220)  评论(0编辑  收藏  举报