篮球(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;
}