Algorithm 在集合划分中比通过差分获得更好的结果

Algorithm 在集合划分中比通过差分获得更好的结果,algorithm,optimization,partition-problem,Algorithm,Optimization,Partition Problem,被认为是NP难的。根据问题的具体情况,我们可以尝试动态规划或一些启发式方法,如差分法(也称为Karmarkar-Karp算法) 后者似乎对具有大数字的实例非常有用(这使得动态规划难以处理),但并不总是完美的。找到更好的解决方案(随机、禁忌搜索、其他近似)的有效方法是什么 附:这个问题背后有一些故事。自2004年7月以来,SPOJ提供了一项挑战。到目前为止,已有1087名用户解决了这一难题,但只有11名用户的得分高于正确的Karmarkar-Karp算法实现(根据目前的得分,Karmarkar-K

被认为是NP难的。根据问题的具体情况,我们可以尝试动态规划或一些启发式方法,如差分法(也称为Karmarkar-Karp算法)

后者似乎对具有大数字的实例非常有用(这使得动态规划难以处理),但并不总是完美的。找到更好的解决方案(随机、禁忌搜索、其他近似)的有效方法是什么


附:这个问题背后有一些故事。自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++)