Algorithm 在集合划分中比通过差分获得更好的结果
被认为是NP难的。根据问题的具体情况,我们可以尝试动态规划或一些启发式方法,如差分法(也称为Karmarkar-Karp算法) 后者似乎对具有大数字的实例非常有用(这使得动态规划难以处理),但并不总是完美的。找到更好的解决方案(随机、禁忌搜索、其他近似)的有效方法是什么Algorithm 在集合划分中比通过差分获得更好的结果,algorithm,optimization,partition-problem,Algorithm,Optimization,Partition Problem,被认为是NP难的。根据问题的具体情况,我们可以尝试动态规划或一些启发式方法,如差分法(也称为Karmarkar-Karp算法) 后者似乎对具有大数字的实例非常有用(这使得动态规划难以处理),但并不总是完美的。找到更好的解决方案(随机、禁忌搜索、其他近似)的有效方法是什么 附:这个问题背后有一些故事。自2004年7月以来,SPOJ提供了一项挑战。到目前为止,已有1087名用户解决了这一难题,但只有11名用户的得分高于正确的Karmarkar-Karp算法实现(根据目前的得分,Karmarkar-K
附:这个问题背后有一些故事。自2004年7月以来,SPOJ提供了一项挑战。到目前为止,已有1087名用户解决了这一难题,但只有11名用户的得分高于正确的Karmarkar-Karp算法实现(根据目前的得分,Karmarkar-Karp得到11.796614分)。如何做得更好?(最需要的答案由接受的提交支持,但请不要透露您的代码。)编辑以下是一个从Karmarkar-Karp差异开始的实现,然后尝试优化生成的分区 时间允许的唯一优化是从一个分区向另一个分区提供1,并在两个分区之间用1替换1 我在一开始实施Karmarkar-Karp时一定不准确,因为仅使用Karmarkar-Karp的结果分数是2.711483而不是11.796614分(OP)。当使用优化时,得分为7.718049 扰流板警告C#提交代码如下
using System;
using System.Collections.Generic;
using System.Linq;
public class Test
{
// some comparer's to lazily avoid using a proper max-heap implementation
public class Index0 : IComparer<long[]>
{
public int Compare(long[] x, long[] y)
{
if(x[0] == y[0]) return 0;
return x[0] < y[0] ? -1 : 1;
}
public static Index0 Inst = new Index0();
}
public class Index1 : IComparer<long[]>
{
public int Compare(long[] x, long[] y)
{
if(x[1] == y[1]) return 0;
return x[1] < y[1] ? -1 : 1;
}
}
public static void Main()
{
// load the data
var start = DateTime.Now;
var list = new List<long[]>();
int size = int.Parse(Console.ReadLine());
for(int i=1; i<=size; i++) {
var tuple = new long[]{ long.Parse(Console.ReadLine()), i };
list.Add(tuple);
}
list.Sort((x, y) => { if(x[0] == y[0]) return 0; return x[0] < y[0] ? -1 : 1; });
// Karmarkar-Karp differences
List<long[]> diffs = new List<long[]>();
while(list.Count > 1) {
// get max
var b = list[list.Count - 1];
list.RemoveAt(list.Count - 1);
// get max
var a = list[list.Count - 1];
list.RemoveAt(list.Count - 1);
// (b - a)
var diff = b[0] - a[0];
var tuple = new long[]{ diff, -1 };
diffs.Add(new long[] { a[0], b[0], diff, a[1], b[1] });
// insert (b - a) back in
var fnd = list.BinarySearch(tuple, new Index0());
list.Insert(fnd < 0 ? ~fnd : fnd, tuple);
}
var approx = list[0];
list.Clear();
// setup paritions
var listA = new List<long[]>();
var listB = new List<long[]>();
long sumA = 0;
long sumB = 0;
// Karmarkar-Karp rebuild partitions from differences
bool toggle = false;
for(int i=diffs.Count-1; i>=0; i--) {
var inB = listB.BinarySearch(new long[]{diffs[i][2]}, Index0.Inst);
var inA = listA.BinarySearch(new long[]{diffs[i][2]}, Index0.Inst);
if(inB >= 0 && inA >= 0) {
toggle = !toggle;
}
if(toggle == false) {
if(inB >= 0) {
listB.RemoveAt(inB);
}else if(inA >= 0) {
listA.RemoveAt(inA);
}
var tb = new long[]{diffs[i][1], diffs[i][4]};
var ta = new long[]{diffs[i][0], diffs[i][3]};
var fb = listB.BinarySearch(tb, Index0.Inst);
var fa = listA.BinarySearch(ta, Index0.Inst);
listB.Insert(fb < 0 ? ~fb : fb, tb);
listA.Insert(fa < 0 ? ~fa : fa, ta);
} else {
if(inA >= 0) {
listA.RemoveAt(inA);
}else if(inB >= 0) {
listB.RemoveAt(inB);
}
var tb = new long[]{diffs[i][1], diffs[i][4]};
var ta = new long[]{diffs[i][0], diffs[i][3]};
var fb = listA.BinarySearch(tb, Index0.Inst);
var fa = listB.BinarySearch(ta, Index0.Inst);
listA.Insert(fb < 0 ? ~fb : fb, tb);
listB.Insert(fa < 0 ? ~fa : fa, ta);
}
}
listA.ForEach(a => sumA += a[0]);
listB.ForEach(b => sumB += b[0]);
// optimize our partitions with give/take 1 or swap 1 for 1
bool change = false;
while(DateTime.Now.Subtract(start).TotalSeconds < 4.8) {
change = false;
// give one from A to B
for(int i=0; i<listA.Count; i++) {
var a = listA[i];
if(Math.Abs(sumA - sumB) > Math.Abs((sumA - a[0]) - (sumB + a[0]))) {
var fb = listB.BinarySearch(a, Index0.Inst);
listB.Insert(fb < 0 ? ~fb : fb, a);
listA.RemoveAt(i);
i--;
sumA -= a[0];
sumB += a[0];
change = true;
} else {break;}
}
// give one from B to A
for(int i=0; i<listB.Count; i++) {
var b = listB[i];
if(Math.Abs(sumA - sumB) > Math.Abs((sumA + b[0]) - (sumB - b[0]))) {
var fa = listA.BinarySearch(b, Index0.Inst);
listA.Insert(fa < 0 ? ~fa : fa, b);
listB.RemoveAt(i);
i--;
sumA += b[0];
sumB -= b[0];
change = true;
} else {break;}
}
// swap 1 for 1
for(int i=0; i<listA.Count; i++) {
var a = listA[i];
for(int j=0; j<listB.Count; j++) {
var b = listB[j];
if(Math.Abs(sumA - sumB) > Math.Abs((sumA - a[0] + b[0]) - (sumB -b[0] + a[0]))) {
listA.RemoveAt(i);
listB.RemoveAt(j);
var fa = listA.BinarySearch(b, Index0.Inst);
var fb = listB.BinarySearch(a, Index0.Inst);
listA.Insert(fa < 0 ? ~fa : fa, b);
listB.Insert(fb < 0 ? ~fb : fb, a);
sumA = sumA - a[0] + b[0];
sumB = sumB - b[0] + a[0];
change = true;
break;
}
}
}
//
if(change == false) { break; }
}
/*
// further optimization with 2 for 1 swaps
while(DateTime.Now.Subtract(start).TotalSeconds < 4.8) {
change = false;
// trade 2 for 1
for(int i=0; i<listA.Count >> 1; i++) {
var a1 = listA[i];
var a2 = listA[listA.Count - 1 - i];
for(int j=0; j<listB.Count; j++) {
var b = listB[j];
if(Math.Abs(sumA - sumB) > Math.Abs((sumA - a1[0] - a2[0] + b[0]) - (sumB - b[0] + a1[0] + a2[0]))) {
listA.RemoveAt(listA.Count - 1 - i);
listA.RemoveAt(i);
listB.RemoveAt(j);
var fa = listA.BinarySearch(b, Index0.Inst);
var fb1 = listB.BinarySearch(a1, Index0.Inst);
var fb2 = listB.BinarySearch(a2, Index0.Inst);
listA.Insert(fa < 0 ? ~fa : fa, b);
listB.Insert(fb1 < 0 ? ~fb1 : fb1, a1);
listB.Insert(fb2 < 0 ? ~fb2 : fb2, a2);
sumA = sumA - a1[0] - a2[0] + b[0];
sumB = sumB - b[0] + a1[0] + a2[0];
change = true;
break;
}
}
}
//
if(DateTime.Now.Subtract(start).TotalSeconds > 4.8) { break; }
// trade 2 for 1
for(int i=0; i<listB.Count >> 1; i++) {
var b1 = listB[i];
var b2 = listB[listB.Count - 1 - i];
for(int j=0; j<listA.Count; j++) {
var a = listA[j];
if(Math.Abs(sumA - sumB) > Math.Abs((sumA - a[0] + b1[0] + b2[0]) - (sumB - b1[0] - b2[0] + a[0]))) {
listB.RemoveAt(listB.Count - 1 - i);
listB.RemoveAt(i);
listA.RemoveAt(j);
var fa1 = listA.BinarySearch(b1, Index0.Inst);
var fa2 = listA.BinarySearch(b2, Index0.Inst);
var fb = listB.BinarySearch(a, Index0.Inst);
listA.Insert(fa1 < 0 ? ~fa1 : fa1, b1);
listA.Insert(fa2 < 0 ? ~fa2 : fa2, b2);
listB.Insert(fb < 0 ? ~fb : fb, a);
sumA = sumA - a[0] + b1[0] + b2[0];
sumB = sumB - b1[0] - b2[0] + a[0];
change = true;
break;
}
}
}
//
if(change == false) { break; }
}
*/
// output the correct ordered values
listA.Sort(new Index1());
foreach(var t in listA) {
Console.WriteLine(t[1]);
}
// DEBUG/TESTING
//Console.WriteLine(approx[0]);
//foreach(var t in listA) Console.Write(": " + t[0] + "," + t[1]);
//Console.WriteLine();
//foreach(var t in listB) Console.Write(": " + t[0] + "," + t[1]);
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
公开课考试
{
//有些比较器懒散地避免使用适当的最大堆实现
公共类索引:IComparer
{
公共整数比较(长[]x,长[]y)
{
如果(x[0]==y[0])返回0;
返回x[0]1){
//抓住马克斯
var b=列表[list.Count-1];
list.RemoveAt(list.Count-1);
//抓住马克斯
var a=列表[list.Count-1];
list.RemoveAt(list.Count-1);
//(b-a)
var diff=b[0]-a[0];
var tuple=new long[]{diff,-1};
diff.Add(新的长[]{a[0],b[0],diff,a[1],b[1]});
//将(b-a)插回
var fnd=list.BinarySearch(tuple,newindex0());
插入(fnd<0?~fnd:fnd,元组);
}
var近似值=列表[0];
list.Clear();
//设置分区
var listA=新列表();
var listB=新列表();
长sumA=0;
长sumB=0;
//Karmarkar Karp从差异重建分区
bool-toggle=false;
对于(int i=diff.Count-1;i>=0;i--){
var inB=listB.BinarySearch(新的long[]{diff[i][2]},Index0.Inst);
var inA=listA.BinarySearch(新的long[]{diff[i][2]},Index0.Inst);
如果(inB>=0&&inA>=0){
切换=!切换;
}
如果(切换==false){
如果(inB>=0){
列表B.删除(inB);
}如果(inA>=0),则为else{
删除列表(inA);
}
var tb=新长[]{diff[i][1],diff[i][4]};
var ta=新长[]{diff[i][0],diff[i][3]};
var fb=listB.BinarySearch(tb,Index0.Inst);
var fa=listA.BinarySearch(ta,Index0.Inst);
插入(fb<0?~fb:fb,tb);
插入(fa<0?~fa:fa,ta);
}否则{
如果(inA>=0){
删除列表(inA);
}如果(inB>=0),则为else{
列表B.删除(inB);
}
var tb=新长[]{diff[i][1],diff[i][4]};
var ta=新长[]{diff[i][0],diff[i][3]};
var fb=listA.BinarySearch(tb,Index0.Inst);
var fa=listB.BinarySearch(ta,Index0.Inst);
插入(fb<0?~fb:fb,tb);
列表b.插入(fa<0?~fa:fa,ta);
}
}
ForEach(a=>sumA+=a[0]);
ForEach(b=>sumB+=b[0]);
//使用give/take 1或swap 1 for 1优化分区
布尔变化=假;
while(DateTime.Now.Subtract(start).TotalSeconds<4.8){
改变=错误;
//从A到B给一个
对于(int i=0;i Math.Abs((sumA-a[0])-(sumB+a[0])){
var fb=listB.BinarySearch(a,Index0.Inst);
插入(fb<0?~fb:fb,a);
列表a.删除(i);
我--;
sumA-=a[0];
sumB+=a[0];
改变=正确;
}else{break;}
}
//从B给A一个
对于(int i=0;i Math.Abs((sumA+b[0])-(sumB-b[0])){
var fa=listA.BinarySearch(b,Index0.Inst);
列表a.插入(fa<0?~fa:fa,b);
清单b.删除(i);
我--;
sumA+=b[0];
sumB-=b[0];
改变=正确;
}else{break;}
}
//以1换1
for(int i=0;i 1;i++)