HDU 1171

来源:互联网 发布:手机后期ps软件 编辑:程序博客网 时间:2024/05/01 18:29

1A。看到直接A掉了还是很激动的呀啊啊。

思路简单,为了要尽量平分,就把总数取一半当做背包的容量,尽量取满就好。

今天下午的目标就是进阶完背包九讲。看了题目之后觉得是多重背包来着,但是有点懒得用多重背包,所以就没有优化,直接拿去一个一个找的,跑了一秒。。。待我再去领悟一番,再优化它。先上代码。

#include <stdio.h>#include <iostream>#include <algorithm>#include <string.h>using namespace std;#define maxn 300000int dp[maxn],val[51],num[51];int main(){    int n;    while(1)    {        scanf("%d",&n);        if(n<0) break;        memset(dp,0,sizeof(dp));        int i,j,k,sum=0;        for(i=1;i<=n;i++) {scanf("%d%d",&val[i],&num[i]);sum+=val[i]*num[i];}        int m=sum/2;        for(i=1;i<=n;i++)            for(j=0;j<=m;j++)               for(k=num[i];k>=0;k--)// for(k=0;k<=num[i];k++)                    if(j>=k*val[i])                dp[j]=max(dp[j],dp[j-k*val[i]]+k*val[i]);        int ans[2];        ans[0]=dp[m];ans[1]=sum-dp[m];        sort(ans,ans+2);        printf("%d %d\n",ans[1],ans[0]);    }    return 0;}

稍稍优化了一下,跑了七百多,,,,可是几十秒的是闹哪样啊!!。。。

#include <stdio.h>#include <iostream>#include <string.h>using namespace std;#define maxn 300000int que[maxn],dp[maxn];int main(){    int n,m;    while(1)    {        scanf("%d",&n);        if(n<0) break;        memset(dp,0,sizeof(dp));        int i,j,k,top=1,sum=0;        int val,num;        for(i=1;i<=n;i++)        {            scanf("%d%d",&val,&num);            for(j=1;j<=num;j++)                que[top++]=j*val;            sum+=val*num;        }        for(i=1;i<top;i++)            for(j=sum/2;j>=que[i];j--)                dp[j]=max(dp[j],dp[j-que[i]]+que[i]);        printf("%d %d\n",sum-dp[sum/2],dp[sum/2]);    }    return 0;}
好吧,这里存在一个问题,就是多重背包好像不是这样优化的。。。。。或者说不止这样优化。 

看背包九讲里面说,将每种类型物品的个数用二进制表示出来,就可以得到任意解。然后要注意公式,每个k都是要满足n-2^(k+1)>0,然后最后一个数是n减去前面几个k的和。这样就可以得到每个取法,而且也不会超出num[i]。多重背包提到说把完全背包和0-1背包结合(给了伪码),额,然后目前,我认为是把每个类型的物品用二进制处理后再用0-1背包处理。。。。

**************************************************

嗷,我知道为什么当某种物品的cost*num>vol的时候要用完全背包做了,因为这种情况就相当于你一直取它知道背包放不下,性质同完全背包。

好吧这么简答的东西当时没多想。。。。。

*************************************************

于是。。。。我按照这样的优化又写了一次,最终跑了一百秒。。。。然后注意sum不要乱除二!!原先我省事把sum直接除二了,然后算的时候再乘回去,尼玛哪里是一样的啊!wa死了。。。。然后学习那个二进制取数是怎么写的。。。

#include <stdio.h>#include <string.h>#define maxn 350000int dp[maxn],que[maxn];int max(int x,int y){    if(x>y) return x;    else return y;}int main(){    int n;    while(1)    {        scanf("%d",&n);        if(n<0) break;        memset(dp,0,sizeof(dp));        memset(que,0,sizeof(que));        int i,j,k;        int val,num,top=1,sum=0;        for(i=1;i<=n;i++)        {            scanf("%d%d",&val,&num);            sum+=val*num;            for(k=1;k<=num;k<<=1)            {                que[top++]=k*val;                num-=k;            }            if(num>0)                que[top++]=num*val;        }        //for(i=1;i<top;i++) printf("**%d",que[i]);printf("\n");        for(i=1;i<=top-1;i++)            for(j=sum/2;j>=que[i];j--)                dp[j]=max(dp[j],dp[j-que[i]]+que[i]);        printf("%d %d\n",sum-dp[sum/2],dp[sum/2]);    }    return 0;}


搜了一下题解,,看到跑的特别快的代码。。刚刚试了一下只有几十ms。。。。先贴着然后我自己领悟领悟


完全背包:

完全背包原先对于它取顺序的方法不是太理解,,或者说没大注意,,这里一看又有新理解了,正式因为顺序,所以你可以忽略它的件数,可以一直往里面取的。每次比较的时候都可能是上次已经取过的结果里面再取所得的结果。

但是这一题里面有点不解,应该可以直接归结于为什么这道题可以用完全背包做?。。。。。

#include<iostream>#include<cstring>#include<algorithm>using namespace std;int dp[500005],w[1005];int main(){int n,m;while(cin>>n&&n>=0){memset(dp,0,sizeof(dp));int i,j,a,v_sum=0;for(i=1;i<=n;++i){cin>>w[i]>>a;v_sum+=w[i]*a;}m=v_sum/2;for(i=1;i<=n;++i){for(j=w[i];j<=m;++j){dp[j]=max(dp[j-w[i]]+w[i],dp[j]); }}cout<<v_sum-dp[m]<<" "<<dp[m]<<endl;}}




0 0
原创粉丝点击