一道背包问题 BunOJ 29376 沙漠之旅

来源:互联网 发布:js在线解密工具 编辑:程序博客网 时间:2024/04/24 19:04

 来源:第十一届北京师范大学程序设计竞赛决赛

题意:给定一个容量L, N个物品的重量W[i],每种物品有无限个,求是否能够总共用4个物品恰好填满容量L。1<=L<=1000,1<=w[i]<=1000
思路:
第一种:考虑到只选4个出来,我们可以分情况讨论:
       (1).用了4种物品,即每种物品一个 (1 1 1 1)
       (2).用了3种物品,(1 1 2)
       (3).用了2种物品,(2 2) (1 3)
       (4).用了1种物品,(4)
判断4种情况是否有成立的,有一种符合要求即可。
第二种:背包DP
    设f[i][j]表示容量为i时,已经装了j个物品的状态是否成立,等于1成立,等于0不成立。
      For(int i=1;i<=n;i++)
           For(int k=1;k<=4;k++)
               For(int j=l;j>=a[i];j--)
                    f[j][k]=f[j][k] | f[j-a[i]][k-1];
第三种:考虑到总共4个物品
   我们先for(int i=1;i<=n;i++)
         For(int j=1;j<=n;j++)
             if(a[i]+a[j]<=l)exist[a[i]+a[j]]=1;
    预处理出任意两个物品可以达到的容量(物品可以是一个种类,也可以是两种).
然后枚举两个物品可以达到的容量,并判断剩下还需填充的是否也是任意两个物品的容量。
  For(int i=0;i<=l;i++)
            If(exist[i] && exist[l-i]) Yes;
     这种方法相比于直接枚举4个物品,非常巧妙的降低了时间复杂度。
这里有一种对称和分治(二分)的思想。那比如我需要判断8个物品的情况,就先预处理出4个物品的情况,而4个物品的情况又由2的物品的情况预处理得来。


   当然此题背包DP的解法是一定要掌握的。

   附上三种解法代码:

   

//解法一
#include<cstdio>#include<iostream>#include<cstring>#include<string>using namespace std;int a[1100];bool exist[1100];int f[1100][1100][5];int n,m;bool work(){//用了三种油 (1 1 2)if(n>=3){for(int i=0;i<n;i++)for(int j=0;j<n;j++){int xx=a[i]*2+a[j];if(xx<m)if(exist[m-xx])return 1;}}//用了两种油 (2 2) (3 1)if(n>=2)for(int i=0;i<n;i++)for(int j=0;j<n;j++){int xx=a[i]*2+a[j]*2;if(xx==m)return 1;xx=a[i]*3+a[j];if(xx==m)return 1;}//用了一种油 (4)for(int i=0;i<n;i++)if(a[i]*4==m)return 1;//用了四种油 (1 1 1 1)    if(n>=4)    {        memset(f,0,sizeof(f));        f[0][0][0]=1;        for(int i=1;i<=n;i++)            for(int j=0;j<=m;j++)                for(int k=0;k<=4;k++)                {                    f[i][j][k]=f[i-1][j][k];                    if(k>=1 && j-a[i-1]>=0)f[i][j][k]=f[i][j][k] | f[i-1][j-a[i-1]][k-1];                }        if(f[n][m][4])return 1;    }return 0;}int main(){int T,l,x;scanf("%d",&T);while(T--){scanf("%d%d%d",&l,&x,&n);memset(exist,0,sizeof(exist));        m=l-x;for(int i=0;i<n;i++){scanf("%d",&a[i]);exist[a[i]]=1;}bool flag=work();if(flag)printf("Yes\n");else printf("No\n");}return 0;}

//解法二
#include<cstdio>#include<iostream>#include<string>#include<cstring>using namespace std;int f[1110][5];int a[1010];int main(){int T;scanf("%d",&T);while(T--){int l,x,n;scanf("%d%d%d",&l,&x,&n);l-=x;for(int i=1;i<=n;i++)scanf("%d",&a[i]);memset(f,0,sizeof(f));f[0][0]=1;for(int i=1;i<=n;i++)for(int k=1;k<=4;k++)for(int j=l;j>=a[i];j--){f[j][k]=f[j-a[i]][k-1]|f[j][k];}if(f[l][4])printf("Yes\n");else printf("No\n");}return 0;}



//解法三
#include<cstdio>#include<iostream>#include<cstring>using namespace std;bool exist[1010];int a[1010];int main(){int T;scanf("%d",&T);while(T--){int l,x,n;scanf("%d%d%d",&l,&x,&n);l-=x;for(int i=1;i<=n;i++)scanf("%d",&a[i]);memset(exist,0,sizeof(exist));bool flag=0;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(a[i]+a[j]<=l)exist[a[i]+a[j]]=1;for(int i=0;i<=l;i++)if(exist[i]&&exist[l-i]){flag=1;break;}flag?puts("Yes"):puts("No");}return 0;}




  



0 0
原创粉丝点击