P7812 [JRKSJ R2] Dark Forest 题解

超级大乱搞题。考察了选手的电脑配置。

首先构造一下把 #3 过掉。构造方法是每次把 x 插到 x2x1 两者之间。初始序列是 {1,2,3}。可以证明 x2x1 一定是靠在一起的。

然后按照出题人题解实现了一下。因为没写过遗传也没看过一个字遗传的代码所以以下内容全是基于出题人题解口胡的。

定义“基因”为一组排列,“变异”为交换一组排列中的两个元素。设两个常数 limitbestbest0.01×limit)。首先随 limit 个基因,把它们按照权值降序排序。每次让所有基因变异 10 次生成 10 个儿子并把所有子代排序。同时采取所谓的“保留精英”策略,把亲代基因中权值最大的 best 个归并进去。种群中排名超过 limit 的部分扔掉。

然而这个所谓的“保留精英”策略其实并不是很好,因为这些所谓的精英可能也没那么优秀。所以在把它们归并进去之前先变异一次然后做一下爬山。这样逼近答案的速度可能会快一点。

很快地把 #1~5 跑出来了。#6 跑了一个中午,#7 跑了一个早上。之后的怎么也跑不出来了。

然后观察题解发现了一个跑得很快的暴力。就是直接枚举两个位置,若交换它们能更优则交换。如果无论如何交换都无法更优就变异 20 次再做。我用这个策略代替了那个神必的爬山(之所以如此神必很可能是因为我爬山写的是假的)。此外再增加了让那 best 个精英产生更多后代(100 个)的策略。这个改良后的算法跑得很快。学校机房的老年机都能在 2min 左右跑完,似乎比那个朴素的暴力稍微快一点?(不确定。)总之比较猛。

可能有点抽象。所以把核心代码放上来。

mt19937 rnd(random_device{}());
inline int random(int l, int r) {
  return rnd() % (r - l + 1) + l;
}

class Gene {
 private:
  int p[maxn];
  i64 val = 0;
  inline void calc() {
    val = 0, p[0] = p[n], p[n + 1] = p[1];
    for (int i = 1; i <= n; i++)
      val += p[i] * w[p[i - 1]] * w[p[i]] * w[p[i + 1]];
    p[0] = p[n + 1] = 0;
  }
  inline void erase(int x) {
    val -= p[x] * w[p[x - 1]] * w[p[x]] * w[p[x + 1]];
    val -= p[x - 1] * w[p[x - 2]] * w[p[x - 1]] * w[p[x]];
    val -= p[x + 1] * w[p[x]] * w[p[x + 1]] * w[p[x + 2]];
  }
  inline void insert(int x) {
    val += p[x] * w[p[x - 1]] * w[p[x]] * w[p[x + 1]];
    val += p[x - 1] * w[p[x - 2]] * w[p[x - 1]] * w[p[x]];
    val += p[x + 1] * w[p[x]] * w[p[x + 1]] * w[p[x + 2]];
  }

 public:
  inline bool operator<(const Gene &q) const {
    return val > q.val;
  }
  inline i64 Val() const {
    return val;
  }
  inline void Output() const {
    for (int i = 1; i <= n; i++)
      out(p[i]), space;
    fflush(stdout);
  }
  inline friend Gene Generate();
  inline friend void Sort(Gene *const, Gene *const);
  inline friend void educate();
  inline Gene Variate() const {
    Gene res = *this;
    static int a, b;
    a = random(1, n), b = random(1, n), swap(res.p[a], res.p[b]);
    res.calc();
    return res;
  }
  inline void Educate() {
    Gene _tmp = *this;
    int *p = _tmp.p, sign = 1;
    while (sign) {
      sign = 0;
      for (int x = 1; x <= n; x++)
        for (int y = x + 1; y <= n; y++) {
          i64 rec = _tmp.val;
          if (x <= 3 || x >= n - 2 || y <= 3 || y >= n - 2 || abs(x - y) <= 4)
            swap(p[x], p[y]), _tmp.calc();
          else
            _tmp.erase(x), _tmp.erase(y), swap(p[x], p[y]), _tmp.insert(x), _tmp.insert(y);
          if (_tmp.val > val)
            *this = _tmp;
          (_tmp.val > rec) ? (sign = 1) : (swap(p[x], p[y]), _tmp.val = rec);
        }
    }
  }
} pop[limit + 10], son[limit * 10 + best * 100 + 10], *top, tmp[limit << 1];

inline Gene Generate() {
  Gene res;
  for (int i = 1; i <= n; i++)
    res.p[i] = i;
  shuffle(res.p + 1, res.p + n + 1, rnd);
  res.calc();
  return res;
}
inline void Sort(Gene *const a, Gene *const end) {
  // 这里省略一个极其冗长的基数排序。
}

inline void init() {
  for (int i = 0; i < limit; i++)
    pop[i] = Generate();
  Sort(pop, pop + limit);
}
inline void variate() {
  top = son;
  for (int i = 0; i < limit; i++) {
    int T = i < best ? 100 : 10;
    while (T--)
      *top++ = pop[i].Variate();
  }
  Sort(son, top);
}
inline void educate() {
  for (int i = 0; i < best; i++) {
    Gene _tmp = pop[i];
    int T     = 10;
    while (T--) {
      _tmp.Educate();
      if (_tmp.val > pop[i].val)
        pop[i] = _tmp;
      for (int i = 1, a, b; i <= 20; i++)
        a = random(1, n), b = random(1, n), swap(_tmp.p[a], _tmp.p[b]);
      _tmp.calc();
    }
  }
  Sort(pop, pop + best);
}
inline void Merge() {
  merge(pop, pop + best, son, son + limit, tmp);
  memcpy(pop, tmp, sizeof(Gene) * limit);
}

signed main() {
  // 前面省略一堆文件输入输出和读入啥的。
  init();
  while (1) {
    variate(), educate(), Merge();
    cerr << pop[0].Val() << endl;
    if (pop[0].Val() >= stop) {
      pop[0].Output();
      break;
    }
  }
}
posted @   MeteorFlower  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示