给定一个整数n(200)和n个数(40).
要求将这些数分组,使得个数最多差1且数字和最接近.
做法很多,这里列举一种
题意要求从n个数中选择n/2个,并使得和与总和的一半最为接近.
状态表示:可以用dp[i][j]表示选择了i个数时和为j的情况是否存在.
边界条件:dp[0][0]=1.
状态表示:dp[i][j]|=dp[i-1][j-save[k]]
复杂度(200*100*200*40)约为(2e8)???
最近几题的复杂度都这么凶险的吗
结果表示:
从dp[n/2][]中搜索,找到dp[n/2][j]为1且j距离sum/2最近的
代码是经典的三段dp:
1.读入/预处理
2.dp
3.输出结果
关于dp环节有几点:
1.需要倒转方向,因为经过了空间优化且每个物品只有一件
2.不用dp到0,因为本题中有个数i的限制.
最后的输出环节:
注意了,主要的复杂度都在dp上,所以dp部分以效率为主.
但是输出环节复杂度不会超过8000,所以应该以易读为主,不要想什么神奇操作,他让干什么就干什么就最好,比如本题中,直接选取两者差最小的就行.
const int N = 128, V = 8096;
int save[N];
int dp[N][V];
int n=read(),sum=0;
for(int i=1;i<=n;i++)
sum+=(save[i]=read());
dp[0][0]=1;
for(int k=1;k<=n;k++)
for(int i=min(n/2,k);i>=1;i--)
for(int j=min(k*40,sum);j>=save[k];j--)
dp[i][j]|=dp[i-1][j-save[k]];
int ans=0;
for(int j=0;j<=sum;j++)
if(dp[n/2][j]&&abs(j*2-sum)<abs(ans*2-sum))
ans=min(j,sum-j);
printf("%d %d\n",ans,sum-ans );