刷题集(7)

#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头,使得两组牛奶总量的差最小。这是一个典型的子集和问题,可以通过动态规划来解决。

核心思想:
  1. ​计算总产量​​:首先计算所有奶牛产奶量的总和S。

  2. ​目标​​:选择n/2头奶牛,使它们的总和尽可能接近S/2。这样,两组的差|S - 2 × 子集和|就会最小。

  3. ​动态规划​​:使用一个二维数组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;
}

此代码仅供参考,请勿纯抄

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值