#356. 挤牛奶
说明
小卡卡终于帮农夫John找到了走丢的那一头奶牛,John为了感谢小卡卡,不仅告诉了他在 Pascal山脉上可能存在Pascal圣地最大的宝藏,还说要请小卡卡喝牛奶。可是农夫John发现他家里所储藏的牛奶已经喝光了,所以要临时给奶牛挤奶。小卡卡实在是太好心了,说要帮农夫John一起挤牛奶。John答应了,他把他的n头奶牛中的n/2头(n是个偶数)分给小卡卡挤,他自己挤剩下的n/2头奶牛。但是每一头奶牛都有不同的产奶量,农夫John为了让小卡卡减轻负担,他希望他们两个所挤的牛奶的总量之差最小。小卡卡得知了每头奶牛的产奶情况之后很快就解决了这个问题。
输入格式
测试数据第一行一个整数n,n为偶数且小于100。表示有n头奶牛。第二行n个整数分别给出每头奶牛的产奶量(产奶量的总和不超过2000)。
输出格式
输出小卡卡和农夫所挤的牛奶量的最小差值。
样例
输入数据 1
6
7 9 2 6 4 2
输出数据 1
0
</p>
解题思路
这个问题要求将n头奶牛(n为偶数)分成两组,每组恰好n/2头,使得两组牛奶总量的差最小。这是一个典型的子集和问题,可以通过动态规划来解决。
核心思想:
-
计算总产量:首先计算所有奶牛产奶量的总和S。
-
目标:选择n/2头奶牛,使它们的总和尽可能接近S/2。这样,两组的差|S - 2 × 子集和|就会最小。
-
动态规划:使用一个二维数组
dp[i][j]表示是否可以用i头奶牛达到总和j。其中i的范围是0到n/2,j的范围是0到S(S不超过2000)。
具体步骤:
-
初始化:
dp[0][0] = true,表示0头奶牛的总和为0是可行的。 -
更新状态:遍历每头奶牛,对于每头奶牛的产奶量v,从后往前更新
dp数组(避免重复计算)。对于i从n/2递减到1,j从S递减到v,如果dp[i-1][j-v]为true,则设置dp[i][j]为true。 -
寻找最优解:遍历所有可能的j(从S/2向下或向上),找到最大的j使得
dp[n/2][j]为true。最小差值为|S - 2 × j|。
复杂度分析:
-
由于n ≤ 100,S ≤ 2000,状态数为n/2 × S ≈ 50 × 2000 = 100,000,在计算上是可行的。
这种方法确保了在有限时间内找到最优解,符合题目要求。
代码样例
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
int a[100],sum=0;
for(int i=0; i<n; i++)
{
cin>>a[i];
sum+=a[i];
}
int m=n/2;
bool dp[51][2001]= {false};
dp[0][0]=true;
for(int i=0; i<n; i++)
{
for(int j=m; j>=1; j--)
{
for(int k=2000; k>=a[i]; k--)
{
if(dp[j-1][k-a[i]]) dp[j][k]=true;
}
}
}
int ans=sum;
for(int k=0; k<=2000; k++)
{
if(dp[m][k])
{
int diff=abs(sum-2*k);
if(diff<ans) ans=diff;
}
}
cout<<ans;
return 0;
}
此代码仅供参考,请勿纯抄
1903

被折叠的 条评论
为什么被折叠?



