篮球比赛分组问题(动态规划)

篮球(5V5)比赛中,每个球员拥有一个战斗力,每个队伍的所有球员战斗力之和为该队伍的总体战斗力。现有10个球员准备分为两队进行训练赛,教练希望2个队伍的战斗力差值能够尽可能的小,以达到最佳训练效果。给出10个球员的战斗力,如果你是教练,你该如何分队,才能达到最佳训练效果?请输出该分队方案下的最小战斗力差值。

输入描述:

10个篮球队员的战斗力(整数,范围[1,10000]),战斗力之间用空格分隔,如:10 9 8 7 6 5 4 3 2 1

不需要考虑异常输入的场景。

输出描述:

最小的战斗力差值,如:1

示例1

输入

10 9 8 7 6 5 4 3 2 1

输出

1

说明

1 2 5 9 10分为一队,3 4 6 7 8分为一队,两队战斗力之差最小,输出差值1。备注:球员分队方案不唯一,但最小战斗力差值固定是1。

这是一道动态规划的问题,难度较高,而且因为要求均分2组,还不太好转化为典型的背包问题。

在知道这是一道动态规划问题之前,我给出了自己的解法(目前我认为这个解法应该是错误的,但暂时还没有找到可以推翻这种解法的测试用例,所以暂时保留了这个解法)。

其基本思路为:

1.首先将队员按照战斗力排序;

2.为了保证战斗力均衡,可以得到战斗力最强的和最弱的必然分到一组,于是得到第一个小的分组(最强和最弱),期战斗力之和为SA,分到A组

3.在剩下的队伍当中两两组合,寻找战斗力最接近SA的组合,分到B组,其战斗力之和为SB,然后寻找最大最小的组合,然后更新SA(或者SB,这个需要在中间比较,看这个组合是放到SA合适还是SB合适),然后在剩下的组合当中寻找两两组合,使得SA和SB的差值最小,直到最后一组;

4.最后一组需要特殊处理一下,因为可能只有2个队员了,此时需要根据SA和SB的值,合理分配成员,最终得到分组结果。

代码为:

//author:autumoon
//联系QQ:4589968
//日期:2021-12-27

int LeastGapGroup()
{
	int count = 10;

	vector<int> vValues;

	do
	{
		int val;
		cin >> val;

		if (val >= 1 && val <= 1e4)
		{
			vValues.push_back(val);
		}

	} while (vValues.size() < count);

	std::sort(vValues.begin(), vValues.end());

	std::vector<int> vFlag;
	for (int i = 0; i < 10; ++i)
	{
		vFlag.push_back(-1);
	}

	//max min together
	vFlag[0] = 0;
	vFlag[9] = 0;

	int nGapMin = 0;
	for (int i = 0; i < 10; ++i)
	{
		nGapMin += vValues[i];
	}

	//fenbie liangzu sum
	int nSum[2] = { 0 };
	nSum[0] = vValues[0] + vValues[9];
	nSum[1] = 0;

	//
	int nCurIndex = 1;

	//left
	vector<int> vLeftValues = GetLeftVector(vValues, vFlag);
	int nLeftCount = vLeftValues.size();
	do
	{
		//zhao b dui
		nLeftCount = vLeftValues.size();

		nGapMin = 0;
		for (int i = 0; i < nLeftCount; ++i)
		{
			nGapMin += vLeftValues[i];
		}

		int nSelIndex1 = -1;
		int nSelIndex2 = -1;

		for (int i = 0; i < nLeftCount; ++i)
		{
			int nSel1 = vLeftValues[i];
			for (int j = i + 1; j < nLeftCount; ++j)
			{
				int nSel2 = vLeftValues[j];
				int nTmp = nSel1 + nSel2 + nSum[nCurIndex];
				if (abs(nTmp - nSum[!nCurIndex]) < nGapMin)
				{
					nGapMin = abs(nTmp - nSum[!nCurIndex]);
					nSelIndex1 = i;
					nSelIndex2 = j;
				}
			}
		}

		if (nSelIndex1 != -1 && nSelIndex2 != -1)
		{
			vFlag[nSelIndex1] = 1;
			vFlag[nSelIndex2] = 1;

			nSum[nCurIndex] += vLeftValues[nSelIndex1] + vLeftValues[nSelIndex2];

			//zuida he zuixiao gei A ? B
			vFlag.clear();
			for (int i = 0; i < vLeftValues.size(); ++i)
			{
				vFlag.push_back(-1);
			}

			if (vLeftValues.size() > 2)
			{
				//jueding gei shui
				nCurIndex = nSum[nCurIndex] < nSum[!nCurIndex] ? nCurIndex : !nCurIndex;

				vFlag[0] = nCurIndex;
				vFlag[vLeftValues.size() - 1] = nCurIndex;

				nSum[nCurIndex] += vLeftValues[0] + vLeftValues[vLeftValues.size() - 1];

				vLeftValues = GetLeftVector(vLeftValues, vFlag);

				nLeftCount = vLeftValues.size();

				nCurIndex = !nCurIndex;
			}
			else if (vLeftValues.size() == 2)
			{
				//yibianjiayige
				if (abs(nSum[nCurIndex] + vLeftValues[0] - (nSum[!nCurIndex] + vLeftValues[1])) < abs(nSum[!nCurIndex] + vLeftValues[1] - (nSum[nCurIndex] + vLeftValues[0])))
				{
					nSum[nCurIndex] += vLeftValues[0];
					nSum[!nCurIndex] += vLeftValues[1];
				}
				else
				{
					nSum[!nCurIndex] += vLeftValues[1];
					nSum[nCurIndex] += vLeftValues[0];
				}

				vLeftValues.clear();

			}
		}
		else
		{
			break;
		}


	} while (nLeftCount > 1);

	cout << abs(nSum[nCurIndex] - nSum[!nCurIndex]) << endl;
}

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值