1A。看到直接A掉了还是很激动的呀啊啊。
思路简单,为了要尽量平分,就把总数取一半当做背包的容量,尽量取满就好。
今天下午的目标就是进阶完背包九讲。看了题目之后觉得是多重背包来着,但是有点懒得用多重背包,所以就没有优化,直接拿去一个一个找的,跑了一秒。。。待我再去领悟一番,再优化它。先上代码。
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
#define maxn 300000
int 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 300000
int 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 350000
int 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;
}
}