有一个数列,要采取怎样的划分方法把它分成2个数列,使得2个数列的各自之和的差最小。
比如一个数列(5,8,13,27,14),通过分成(5,13,14)和(8,27),这样2个数列各自的和再作差就等于35-32=3,这样差值最小。
暂时有两种想法可以做,现在只给出第一种的代码(C#)
1. 动态规划
(1)就是根据输入数组的长度(N),把这个问题看成一个N歩动态规划的问题。
(2)对于动态规划,需要有状态变量记录当前的状态(这里有当前歩i,前面的选择情况,当前的差值);
(3)对于每一步的选择有两种,比如第i歩时,选择将第i个数放入数组1或者数组2。
最后的实现就是递归状态变量得到。
2. 枚举所有可能的差值
(1) 对于有限的数组,算出所有数分成两组可能的绝对值是可能的。
(2) 通过递归的形式,将长度大于2的数组,划分为两个较小的数组,然后分别计算它们可能得到的差值。然后再定义,两个子数组差值合并的计算方法和数组长度为2时的计算方法即可。
(3) 对于两个子数组差值合并的计算,把差值按照一定顺序排列,建立每个差值和整个数组分组的对应关系,所以不用记录每个差值对应的分组。
最后的实现就是递归数组得到。
struct choiceSet
{
public int choice;
public int value;
public int position;
}
public static int minVal = 0, choiceFinal = 0;
static void Main(string[] args)
{
List<int> originalList = new List<int>();
string input = Console.ReadLine();
char splitcomma = ',';
string[] temp = input.Split(splitcomma);
foreach (string s in temp)
{
originalList.Add(getNum(s));
minVal += Convert.ToInt32(getNum(s));
}
choiceSet cs0;
cs0.choice = 1;
cs0.value = 0;
cs0.position = 0;
stepOne(originalList, cs0);
writeResult(originalList, choiceFinal);
Console.ReadKey();
}
static void writeResult(List<int> li, int choice)
{
Console.WriteLine("[Value,Group]");
for (int i = 0; i < li.Count; i++)
{
Console.WriteLine("[{0},{1}]", li[li.Count-1-i], choice % 2 + 1);
choice=(choice-choice%2)/2;
}
Console.WriteLine("The difference is {0}.",minVal);
}
static void stepOne(List<int> li, choiceSet cs)
{
if (cs.position == 0)
{
choiceSet cs1;
cs1.choice = cs.choice * 2 + 0;
cs1.value = cs.value + li[cs.position];
cs1.position = cs.position + 1;
stepOne(li, cs1);
}
else if (cs.position < li.Count - 1)
{
choiceSet cs1, cs2;
cs1.choice = cs.choice * 2 + 0;
cs1.value = cs.value + li[cs.position];
cs1.position = cs.position + 1;
cs2.choice = cs.choice * 2 + 1;
cs2.value = cs.value - li[cs.position];
cs2.position = cs.position + 1;
stepOne(li, cs1);
stepOne(li, cs2);
}
else
{
if (Math.Abs(cs.value + li[cs.position]) <= Math.Abs(cs.value + li[cs.position]))
{
int fValue = cs.value + li[cs.position];
if (Math.Abs(fValue) < Math.Abs(minVal))
{
minVal = fValue;
choiceFinal = cs.choice * 2 + 0;
}
}
else
{
int fValue = cs.value - li[cs.position];
if (Math.Abs(fValue) < Math.Abs(minVal))
{
minVal = fValue;
choiceFinal = cs.choice * 2 + 1;
}
}
}
}
static int getNum(string s)
{
char[] temp = s.ToCharArray();
int sign = 1;
int sum = 0;
foreach (char c in temp)
{
if (c == '-')
sign = -1;
else
sum = sum * 10 + ((int)c - 48);
}
return sum * sign;
}