P2392 kkksc03考前临时抱佛脚

Captain_Paul

2017-10-15 14:10:22

Solution

这道题可以运用dp方法求解,建议与P1489猫狗大战配合食用,效果更佳。 这道题的目的就是将每一科目的所有数分成两部分,使得两部分的差最小。 首先,对于每一科目,用布尔类型数组f[i][j]表示i个数中取得总和j是否可行,可行则为true,否则为false。 显然,初始化使得f[0][0]=1. 本题的输入数据可以用二维数组来存储,那么状态转移方程可以表示为:f[j][k]=f[j][k]||f[j-1][k-a[t][i]] 然后我们可以从sum[t]>>1开始枚举,若f[j][i](1<=j<=n)为true,则答案为max(i,sum[t]-i),退出函数即可。 将以上过程重复四次,累加所得答案,输出andAC! 代码如下: ```cpp #include<iostream> #include<cstdio> #include<cstring> using namespace std; int f[25][1205]; int s1,s2,s3,s4,a[5][25]; int ans[5],sum[5],answer; void dp(int t,int n) { int nn; if (n==1) { ans[t]=sum[t]; return; } memset(f,0,sizeof(f)); f[0][0]=1; //if (n&1) nn=n/2+1; //else nn=n/2; for (int i=1;i<=n;i++) for (int j=n;j>=1;j--) for (int k=sum[t]>>1;k>=a[t][i];k--) f[j][k]=f[j][k]||f[j-1][k-a[t][i]]; for (int i=sum[t]>>1;i>=0;i--) for (int j=1;j<=n;j++) if (f[j][i]) { ans[t]=max(i,sum[t]-i); return; } } int main() { int i,j; scanf("%d%d%d%d",&s1,&s2,&s3,&s4); for (i=1;i<=s1;i++) scanf("%d",&a[1][i]),sum[1]+=a[1][i]; for (i=1;i<=s2;i++) scanf("%d",&a[2][i]),sum[2]+=a[2][i]; for (i=1;i<=s3;i++) scanf("%d",&a[3][i]),sum[3]+=a[3][i]; for (i=1;i<=s4;i++) scanf("%d",&a[4][i]),sum[4]+=a[4][i]; dp(1,s1); dp(2,s2); dp(3,s3); dp(4,s4); //cout<<ans[1]<<" "<<ans[2]<<" "<<ans[3]<<" "<<ans[4]<<endl; for (i=1;i<=4;i++) answer+=ans[i]; printf("%d\n",answer); return 0; } ```