qzezoj 1337 拔河
今天的\(ACM\)模拟赛,最后一道题让我们抓耳挠腮了好长时间。这道题看似能贪心(\(50\)分),又好像能搜索(\(50\)分),其实就是\(dp\),一道\(01\)背包的变体,问题中一个人要么到一边,要么到另一边。这不就是\(01\)背包嘛!不过还要控制人数。
下面有一个在\(ACM\)上过的,但是被民间数据hank掉的:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,sum;
while(~scanf("%d",&n)){
sum=0;
int a[117];
int xc[50017]={0};
for(int i=1;i<=n;i++){
cin>>a[i];
sum+=a[i];
}
for(int i=1;i<=n;i++){
for(int j=sum/2;j>=a[i];j--){
xc[j]=max(xc[j],xc[j-a[i]]+a[i]);
}
}
cout<<xc[sum/2]<<" "<<sum-xc[sum/2]<<endl;
}
return 0;
}
\(hank\)数据:
4
10
20
500
600
输出:
520 610
而他的输出却是
530 600
这是因为他没控制人数!
下面是控制人数的\(AC\)代码(虽然慢,但稳妥):
#include<bits/stdc++.h>
using namespace std;
int n,f[51][45000],ans,tot,a[514];
bool cmp(int a,int b) {
return a<b;
}
int main() {
while(scanf("%d",&n)!=EOF) {
tot=ans=0;
memset(f,0,sizeof(f));
for(int i=1; i<=n; i++) scanf("%d",&a[i]),ans+=a[i];
f[0][0]=1;
for(int i=1; i<=n/2; i++) {
for(int j=ans/2; j>=0; j--) {
if(f[i-1][j]) {
for(int k=1; k<=n; k++) {
f[i][j+a[k]]=1;
}
}
}
}
for(int i=ans/2; i<=ans; i++) {
if(f[n/2][i]) {
tot=i;
break;
}
}
printf("%d %d\n",min(tot,ans-tot),max(tot,ans-tot));
}
return 0;
}