P7812 [JRKSJ R2] Dark Forest 题解
超级大乱搞题。考察了选手的电脑配置。
首先构造一下把 #3 过掉。构造方法是每次把
然后按照出题人题解实现了一下。因为没写过遗传也没看过一个字遗传的代码所以以下内容全是基于出题人题解口胡的。
定义“基因”为一组排列,“变异”为交换一组排列中的两个元素。设两个常数
然而这个所谓的“保留精英”策略其实并不是很好,因为这些所谓的精英可能也没那么优秀。所以在把它们归并进去之前先变异一次然后做一下爬山。这样逼近答案的速度可能会快一点。
很快地把 #1~5 跑出来了。#6 跑了一个中午,#7 跑了一个早上。之后的怎么也跑不出来了。
然后观察题解发现了一个跑得很快的暴力。就是直接枚举两个位置,若交换它们能更优则交换。如果无论如何交换都无法更优就变异 20 次再做。我用这个策略代替了那个神必的爬山(之所以如此神必很可能是因为我爬山写的是假的)。此外再增加了让那
可能有点抽象。所以把核心代码放上来。
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;
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现