n皇后问题,n=4000000,5s
好像有构造方法,但是爬山的话就可以用经典的那个做法,就是随机交换两个列皇后的行编号,然后看每条斜线皇后个数的平方和是否变小,如果变小,就交换,这个100000随便跑,但是4000000比较困难,所以需要再用很多技巧优化,最后可以在五秒内完成。
code
#include <bits/stdc++.h>
#define FOR(i, j, k) for(int i = (j); i <= (k); ++i)
#define ROF(i, j, k) for(int i = (j); i >= (k); --i)
#define vi vector <int>
#define ll long long
#define pii pair<int, int>
#define sz(a) ((int) (a).size())
#define me(a, x) memset(a, x, sizeof(a))
#define eb emplace_back
using namespace std;
const int N = 4000000 + 10;
int n;
int p[N];
int buc1[N * 2], buc2[N * 2];
vector<int> id1[N * 2], id2[N * 2];
int vis[N * 2];
ll sum = 0;
void mdf1(int x, int v) {
sum -= (ll)buc1[x] * buc1[x];
buc1[x] += v;
sum += (ll)buc1[x] * buc1[x];
}
void mdf2(int x, int v) {
sum -= (ll)buc2[x] * buc2[x];
buc2[x] += v;
sum += (ll)buc2[x] * buc2[x];
}
void mdf(int x, int v) {
mdf1(x + p[x], v);
mdf2(x - p[x] + n, v);
}
mt19937 gen(__builtin_ia32_rdtsc());
int rd(int l, int r) {
uniform_int_distribution<int> rng(l, r);
return rng(gen);
}
void Swap(int u, int v) {
mdf(u, -1), mdf(v, -1);
swap(p[u], p[v]);
mdf(u, 1), mdf(v, 1);
}
vector<int> id;
int check(int x) {
return buc1[x + p[x]] == 1 && buc2[x - p[x] + n] == 1;
}
int Try(int u, int v) {
ll tsum = sum;
Swap(u, v);
if(tsum < sum) {
Swap(u, v);
return 0;
}
return 1;
}
void del(int u) {
vector<int> &v1 = id1[u + p[u]];
v1.erase(find(v1.begin(), v1.end(), u));
vector<int> &v2 = id2[u - p[u] + n];
v2.erase(find(v2.begin(), v2.end(), u));
}
void ins(int u) {
vector<int> &v1 = id1[u + p[u]];
v1.push_back(u);
vector<int> &v2 = id2[u - p[u] + n];
v2.push_back(u);
}
void Check(int x) {
for(auto u : id1[x + p[x]]) {
if(!check(u) && !vis[u]) {
id.emplace_back(u);
vis[u] = 1;
}
}
for(auto u : id2[x - p[x] + n]) {
if(!check(u) && !vis[u]) {
id.emplace_back(u);
vis[u] = 1;
}
}
}
void shake() {
int u = id[rd(0, sz(id) - 1)], v;
do {
v = rd(1, n);
} while(vis[v]);
ll tsum = sum;
Swap(u, v);
if(tsum < sum) {
Swap(u, v);
} else {
swap(p[u], p[v]);
del(u), del(v);
swap(p[u], p[v]);
ins(u), ins(v);
Check(u), Check(v);
}
}
void pretrain() {
int cnt = 0;
while(sum > 2 * n) {
cnt += 1;
int u, v;
do {
swap(id[rd(0, sz(id) - 1)], id[sz(id) - 1]);
swap(id[rd(0, sz(id) - 2)], id[sz(id) - 2]);
if(check(id[sz(id) - 1])) {
vis[id.back()] = 0;
id.pop_back();
continue;
}
if(check(id[sz(id) - 2])) {
swap(id[sz(id) - 1], id[sz(id) - 2]);
vis[id.back()] = 0;
id.pop_back();
continue;
}
u = id[sz(id) - 1], v = id[sz(id) - 2];
break;
} while(1);
ll tsum = sum;
Swap(u, v);
if(tsum < sum) {
Swap(u, v);
}
if(sz(id) <= 100) break;
// if(cnt % 1000000 == 0) cout << cnt << " " << sum << " " << sz(id) << endl;
}
}
int main() {
n = 4000000;
FOR(i, 1, n) p[i] = i, id.emplace_back(i), vis[i] = 1;
shuffle(p + 1, p + n + 1, gen);
shuffle(id.begin(), id.end(), gen);
FOR(i, 1, n) {
mdf(i, 1);
}
pretrain();
FOR(i, 1, n) {
id1[i + p[i]].emplace_back(i);
id2[i - p[i] + n].emplace_back(i);
}
int cnt = 0;
ll presum = -1;
while(sum > 2 * n) {
cnt += 1;
int u, v;
do {
swap(id[rd(0, sz(id) - 1)], id[sz(id) - 1]);
swap(id[rd(0, sz(id) - 2)], id[sz(id) - 2]);
if(check(id[sz(id) - 1])) {
vis[id.back()] = 0;
id.pop_back();
continue;
}
if(check(id[sz(id) - 2])) {
swap(id[sz(id) - 1], id[sz(id) - 2]);
vis[id.back()] = 0;
id.pop_back();
continue;
}
u = id[sz(id) - 1], v = id[sz(id) - 2];
break;
} while(1);
ll tsum = sum;
Swap(u, v);
if(tsum < sum) {
Swap(u, v);
} else {
swap(p[u], p[v]);
del(u), del(v);
swap(p[u], p[v]);
ins(u), ins(v);
Check(u), Check(v);
}
if(sz(id) <= 100) shake();
if(cnt % 100000 == 0) {
if(sum == presum) {
// break;
}
presum = sum;
}
// if(cnt % 1000000 == 0) cout << cnt << " " << sum << " " << sz(id) << endl;
}
// while(sum > 2 * n) {
// cnt += 1;
// int u, v;
// do {
// u = rd(1, n), v = rd(1, n);
// } while(u == v);
// ll tsum = sum;
// Swap(u, v);
// if(tsum < sum) {
// Swap(u, v);
// }
// // if(cnt % 100000 == 0) {
// // if(sum == presum) {
// // break;
// // }
// // presum = sum;
// // }
// if(cnt % 1000000 == 0) cout << cnt << " " << sum << " " << sz(id) << endl;
// }
cout << cnt << " " << sum << " " << sz(id) << endl;
// for(auto u : id) cout << u <<" " << p[u] << endl;
cout << "Done !" << endl;
}