Java,遗传算法,80 + 81 + 79 + 78 + 80 = 398(以前是418)
在尝试了许多不同的想法并且大多数都失败之后,我决定使用这种算法:从输入数组开始,尝试所有可能的反转,并以最小的运行次数保留一定数量的结果,然后对这些结果执行相同的操作,直到我们得到一个排序数组。
“游程”是指在排序数组中完全出现或颠倒的最大子数组。基本上,它们是最大排序的子数组,但是如果元素重复,则中间的元素数量应该匹配。例如,如果排序后的数组中2, 2, 3, 3, 4, 4
然后4, 3, 3, 2
是一个运行但2, 2, 3, 4
不是(既不是2, 3, 2
)。
在此版本中,我优化了算法,使其仅在运行边界处反转,并且仅当反转的运行可以与新相邻的运行合并时才进行。同样,在每个步骤中调整并合并运行,以避免从修改后的数组重新计算它们。这使我可以将“人口规模”从30增加到大约3000,并以不同的规模运行多个模拟。
import java.io.*;
import java.util.*;
public class SubReversal {
static int n;
static int[] a;
static int[] srt;
static List<int[]> rev;
static Map<Integer, Integer> idx;
static Map<Integer, Integer> count;
static final int NB = 2000;
static State[] best = new State[NB + 1];
static int ns;
static class Run {
int start;
int end;
int dir;
int nstart = 1;
int nend = 1;
Run(final int start) {
this.start = start;
}
Run(final Run r) {
start = r.start;
end = r.end;
dir = r.dir;
nstart = r.nstart;
nend = r.nend;
}
Run copy() {
return new Run(this);
}
Run reverse() {
int t = start;
start = end;
end = t;
t = nstart;
nstart = nend;
nend = t;
dir = -dir;
return this;
}
boolean canJoin(final Run r) {
if (dir * r.dir == -1) {
return false;
}
final int t = idx.get(a[r.start]) - idx.get(a[end]);
if (Math.abs(t) > 1) {
return false;
}
if (t != 0 && dir + r.dir != 0 && t != dir && t != r.dir) {
return false;
}
if (t == 0) {
if (dir * r.dir == 0) {
return true;
}
return nend + r.nstart == count.get(a[end]);
}
return (dir == 0 || nend == count.get(a[end])) && (r.dir == 0 || r.nstart == count.get(a[r.start]));
}
Run join(final Run r) {
if (a[start] == a[r.start]) {
nstart += r.nstart;
}
if (a[end] == a[r.end]) {
nend += r.nend;
}
else {
nend = r.nend;
}
end = r.end;
if (dir == 0) {
dir = r.dir;
}
if (dir == 0 && a[start] != a[end]) {
dir = idx.get(a[end]) - idx.get(a[start]);
}
return this;
}
@Override
public String toString() {
return start + "(" + nstart + ") - " + end + '(' + nend + "): " + dir;
}
}
static class State implements Comparable<State> {
int[] b;
int[] rv;
State p;
List<Run> runs;
public State(final int[] b, final int[] rv, final State p, final List<Run> runs) {
this.b = Arrays.copyOf(b, b.length);
this.rv = rv;
this.p = p;
this.runs = runs;
}
@Override
public int compareTo(final State o) {
return runs.size() - o.runs.size();
}
@Override
public String toString() {
return Arrays.toString(b) + " - " + Arrays.toString(rv) + " - " + runs.size();
}
int getCount() {
return p == null ? 0 : p.getCount() + 1;
}
}
static void reverse(int x, int y) {
while (x < y) {
int t = a[x];
a[x] = a[y];
a[y] = t;
x++;
y--;
}
}
static List<Run> runs() {
final List<Run> l = new ArrayList<>();
Run run = new Run(0);
for (int i = 1; i < n; ++i) {
final int t = idx.get(a[i]) - idx.get(a[i - 1]);
if (Math.abs(t) > 1) {
run.end = i - 1;
l.add(run);
run = new Run(i);
}
else if (t == 0) {
run.nend++;
if (run.dir == 0) {
run.nstart++;
}
}
else {
if (run.dir == 0) {
run.dir = t;
}
else if (run.dir != t || run.nend != count.get(a[i - 1])) {
run.end = i - 1;
l.add(run);
run = new Run(i);
}
run.nend = 1;
}
}
run.end = n - 1;
l.add(run);
return l;
}
static void show() {
if (!Arrays.equals(a, srt)) {
System.out.println("bug!");
System.out.println(Arrays.toString(a));
throw new RuntimeException();
}
System.out.println("Sorted: " + Arrays.toString(a));
System.out.println(rev.size() + " reversal(s):");
for (int[] x : rev) {
System.out.println(Arrays.toString(x));
}
}
static void sort() {
State bestest = null;
final int[] a1 = Arrays.copyOf(a, n);
final int[] sizes = {10, 20, 30, 50, 100, 200, 300, 500, 1000, 2000};
for (int nb : sizes) {
System.arraycopy(a1, 0, a, 0, n);
ns = 1;
best[0] = new State(a, null, null, runs());
while (best[0].runs.size() > 1) {
final State[] s = Arrays.copyOf(best, ns);
ns = 0;
for (State x : s) {
System.arraycopy(x.b, 0, a, 0, n);
final int m = x.runs.size();
for (int i = 0; i < m; ++i) {
for (int j = i; j < m; ++j) {
boolean b = false;
if (i > 0) {
final Run r = x.runs.get(j);
r.reverse();
b = x.runs.get(i - 1).canJoin(r);
r.reverse();
}
if (!b && j < m - 1) {
final Run r = x.runs.get(i);
r.reverse();
b = r.canJoin(x.runs.get(j + 1));
r.reverse();
}
if (!b) {
continue;
}
final List<Run> l = new ArrayList<>(x.runs);
final int rstart = l.get(i).start;
final int rend = l.get(j).end;
final int t = rstart + rend;
reverse(rstart, rend);
for (int k = i; k <= j; ++k) {
final Run r = x.runs.get(i + j - k).copy().reverse();
r.start = t - r.start;
r.end = t - r.end;
l.set(k, r);
}
if (j < m - 1 && l.get(j).canJoin(l.get(j + 1))) {
l.get(j).join(l.get(j + 1));
l.remove(j + 1);
}
if (i > 0 && l.get(i - 1).canJoin(l.get(i))) {
l.set(i - 1, l.get(i - 1).copy().join(l.get(i)));
l.remove(i);
}
if (ns < nb || l.size() < best[ns - 1].runs.size()) {
best[ns++] = new State(a, new int[]{rstart, rend}, x, l);
Arrays.sort(best, 0, ns);
if (ns > nb) {
ns = nb;
}
}
reverse(rstart, rend);
}
}
}
if (ns == 0) {
for (State x : s) {
System.arraycopy(x.b, 0, a, 0, n);
final List<Run> l = new ArrayList<>(x.runs);
final int rstart = l.get(0).start;
final int rend = l.get(0).end;
final int t = rstart + rend;
reverse(rstart, rend);
final Run r = x.runs.get(0).copy().reverse();
r.start = t - r.start;
r.end = t - r.end;
l.set(0, r);
best[ns++] = new State(a, new int[]{rstart, rend}, x, l);
reverse(rstart, rend);
}
Arrays.sort(best, 0, ns);
}
}
State r = null;
for (int i = 0; i < ns; ++i) {
if (Arrays.equals(best[i].b, srt)) {
r = best[i];
break;
}
}
if (r == null) {
final State x = best[0];
System.arraycopy(x.b, 0, a, 0, n);
reverse(0, n - 1);
r = new State(a, new int[]{0, n - 1}, x, runs());
}
if (!Arrays.equals(r.b, srt)) {
throw new RuntimeException("bug");
}
if (bestest == null || r.getCount() < bestest.getCount()) {
bestest = r;
}
}
while (bestest.p != null) {
rev.add(bestest.rv);
bestest = bestest.p;
}
Collections.reverse(rev);
a = a1;
for (int[] x : rev) {
reverse(x[0], x[1]);
}
if (!Arrays.equals(a, srt)) {
throw new RuntimeException("bug");
}
}
static void init(final String s) {
final String[] b = s.split(s.contains(",") ? "," : " ");
n = b.length;
a = new int[n];
count = new HashMap<>();
for (int i = 0; i < n; ++i) {
a[i] = Integer.parseInt(b[i].trim());
final Integer x = count.get(a[i]);
count.put(a[i], x == null ? 1 : x + 1);
}
srt = Arrays.copyOf(a, n);
Arrays.sort(srt);
idx = new HashMap<>();
int j = 0;
for (int i = 0; i < n; ++i) {
if (i == 0 || srt[i] != srt[i - 1]) {
idx.put(srt[i], j++);
}
}
rev = new ArrayList<>();
}
static void test5() {
final String[] t = {"133, 319, 80, 70, 194, 333, 65, 21, 345, 142, 82, 491, 92, 167, 281, 386, 48, 101, 394, 130, 111, 139, 214, 337, 180, 24, 443, 35, 376, 13, 166, 59, 452, 429, 406, 256, 133, 435, 446, 304, 350, 364, 447, 471, 236, 177, 317, 342, 294, 146, 280, 32, 135, 399, 78, 251, 467, 305, 366, 309, 162, 473, 27, 67, 305, 497, 112, 399, 103, 178, 386, 343, 33, 134, 480, 147, 466, 244, 370, 140, 227, 292, 28, 357, 156, 367, 157, 60, 214, 280, 153, 445, 301, 108, 77, 404, 496, 3, 226, 37",
"468, 494, 294, 42, 19, 23, 201, 47, 165, 118, 414, 371, 163, 430, 295, 333, 147, 336, 403, 490, 370, 128, 261, 91, 173, 339, 40, 54, 331, 236, 255, 33, 237, 272, 193, 91, 232, 452, 79, 435, 160, 328, 47, 179, 162, 239, 315, 73, 160, 266, 83, 451, 317, 255, 491, 70, 18, 275, 339, 298, 117, 145, 17, 178, 232, 59, 109, 271, 301, 437, 63, 103, 130, 15, 265, 281, 365, 444, 180, 257, 99, 248, 378, 158, 210, 466, 404, 263, 29, 117, 417, 357, 44, 495, 303, 428, 146, 215, 164, 99",
"132, 167, 361, 145, 36, 56, 343, 330, 14, 412, 345, 263, 306, 462, 101, 453, 364, 389, 432, 32, 200, 76, 268, 291, 35, 13, 448, 188, 11, 235, 184, 439, 175, 159, 360, 46, 193, 440, 334, 128, 346, 192, 263, 466, 175, 407, 340, 393, 231, 472, 122, 254, 451, 485, 257, 67, 200, 135, 132, 421, 205, 398, 251, 286, 292, 488, 480, 56, 284, 484, 157, 264, 459, 6, 289, 311, 116, 138, 92, 21, 307, 172, 352, 199, 55, 38, 427, 214, 233, 404, 330, 105, 223, 495, 334, 169, 168, 444, 268, 248",
"367, 334, 296, 59, 18, 193, 118, 10, 276, 180, 242, 115, 233, 40, 225, 244, 147, 439, 297, 115, 354, 248, 89, 423, 47, 458, 64, 33, 463, 142, 5, 13, 89, 282, 186, 12, 70, 289, 385, 289, 274, 136, 39, 424, 174, 186, 489, 73, 296, 39, 445, 308, 451, 384, 451, 446, 282, 419, 479, 220, 35, 419, 161, 14, 42, 321, 202, 30, 32, 162, 444, 215, 218, 102, 140, 473, 500, 480, 402, 1, 1, 79, 50, 54, 111, 189, 147, 352, 61, 460, 196, 77, 315, 304, 385, 275, 65, 145, 434, 39",
"311, 202, 126, 494, 321, 330, 290, 28, 400, 84, 6, 160, 432, 308, 469, 459, 80, 48, 292, 229, 191, 240, 491, 231, 286, 413, 170, 486, 59, 54, 36, 334, 135, 39, 393, 201, 127, 95, 456, 497, 429, 139, 81, 293, 359, 477, 404, 129, 129, 297, 298, 495, 424, 446, 57, 296, 10, 269, 350, 337, 39, 386, 142, 327, 22, 352, 421, 32, 171, 452, 2, 484, 337, 359, 444, 246, 174, 23, 115, 102, 427, 439, 71, 478, 89, 225, 7, 118, 453, 350, 109, 277, 338, 474, 405, 380, 256, 228, 277, 3"};
int r = 0;
for (String s : t) {
init(s);
sort();
System.out.println(rev.size());
r += rev.size();
}
System.out.println("total: " + r);
}
public static void main(final String... args) throws IOException {
System.out.print("Input: ");
final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
final String s = br.readLine();
final long t = System.currentTimeMillis();
if (s.isEmpty()) {
System.out.println("Running tests");
test5();
}
else {
init(s);
sort();
show();
}
System.out.println("Time: " + (System.currentTimeMillis() - t + 500) / 1000 + " sec");
}
}
输入是一个由逗号和/或空格分隔的数字列表(与stdin分开)。如果输入为空,则程序将运行5个测试。每个人大约需要40秒。