「THUSCH 2017」巧克力(斯坦纳树dp+随机化+中位数二分)
暴力:状态记录当前选了哪些颜色的点,用斯坦纳树去转移,应该能过个40分。
对于第2问,考虑先二分答案mid,把<=mid的数权值设为-1,>mid的取值设为1,相当于在联通块点数最少的同时,权值和最小。
若最小权值和<=0,则说明真正的答案\(>=mid\),调整二分区间即可。
对于这种恰好选k个不同的颜色,且k很小的问题,不难想到随机化。
先给每个颜色随机一个新的\(\in [1,k]\)的颜色,把问题转化为把k种颜色全部选到,那么状态就是\(2^k\)的了。
一次错误的概率是\(1-{k! \over k^k}\),做\(T=150\)次,大概有千分之三的错误率。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
const int N = 235;
int T;
int n, m, k;
int c[N][N], a[N][N], v[N][N];
void Init() {
scanf("%d %d %d", &n, &m, &k);
fo(i, 1, n) fo(j, 1, m) scanf("%d", &c[i][j]);
fo(i, 1, n) fo(j, 1, m) scanf("%d", &a[i][j]);
}
int b[N * N];
int rand(int x, int y) {
return ((ll) RAND_MAX * rand() + rand()) % (y - x + 1) + x;
}
void srand_b() {
int c = n * m / k;
fo(i, 1, k * c) b[i] = (i - 1) / c;
fo(i, k * c + 1, n * m) b[i] = rand(0, k - 1);
random_shuffle(b + 1, b + n * m + 1);
}
const int inf = 1e9;
int a2[6];
#define pii pair<int, int>
#define fs first
#define se second
pii operator + (pii a, pii b) {
return pii(a.fs + b.fs, a.se + b.se);
}
pii f[32][N][N];
struct P {
int x, y;
pii v;
};
bool operator < (P a, P b) {
return a.v > b.v;
}
priority_queue<P> q;
int us[N][N];
int mov[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
pii dp() {
ff(s, 0, a2[k]) fo(i, 1, n) fo(j, 1, m)
f[s][i][j] = pii(inf, 0);
fo(i, 1, n) fo(j, 1, m) if(c[i][j] != -1) {
f[0][i][j] = pii(1, v[i][j]);
f[a2[b[c[i][j]]]][i][j] = pii(1, v[i][j]);
}
ff(s, 0, a2[k]) {
fo(i, 1, n) fo(j, 1, m) if(c[i][j] != -1) {
for(int p = s - 1; p > 0; p = (p - 1) & s) {
pii z = f[p][i][j] + f[s ^ p][i][j];
z.fs --; z.se -= v[i][j];
if(z < f[s][i][j]) f[s][i][j] = z;
}
q.push((P) {i, j, f[s][i][j]});
}
fo(i, 1, n) fo(j, 1, m) us[i][j] = 0;
while(q.size()) {
P b = q.top(); q.pop();
if(us[b.x][b.y]) continue;
us[b.x][b.y] = 1;
fo(k, 0, 3) {
int l = b.x + mov[k][0], r = b.y + mov[k][1];
if(l && r && l <= n && r <= m && c[l][r] != -1) {
pii z = b.v;
z.fs ++; z.se += v[l][r];
if(z < f[s][l][r]) {
f[s][l][r] = z;
q.push((P) {l, r, f[s][l][r]});
}
}
}
}
}
pii ans = pii(inf, inf);
fo(i, 1, n) fo(j, 1, m)
ans = min(ans, f[a2[k] - 1][i][j]);
return ans;
}
int W;
int ans1, ans2;
int p[N], p0;
void work() {
for(int l = 1, r = p0; l <= r; ) {
int mi = l + r >> 1;
fo(i, 1, n) fo(j, 1, m) {
v[i][j] = a[i][j] <= p[mi] ? -1 : 1;
}
pii z = dp();
if(z.se > 0) {
l = mi + 1;
} else {
if(z.fs < ans1) ans1 = z.fs, ans2 = p[mi]; else
if(z.fs == ans1 && p[mi] < ans2) ans2 = p[mi];
r = mi - 1;
}
}
}
int main() {
srand(time(0) + clock());
a2[0] = 1; fo(i, 1, 5) a2[i] = a2[i - 1] * 2;
for(scanf("%d", &T); T; T --) {
Init();
W = 233 * 150 / (n * m);
p0 = 0;
fo(i, 1, n) fo(j, 1, m) if(c[i][j] != -1)
p[++ p0] = a[i][j];
sort(p + 1, p + p0 + 1);
p0 = unique(p + 1, p + p0 + 1) - (p + 1);
ans1 = ans2 = 1e9;
fo(ii, 1, W) {
srand_b();
work();
}
if(ans1 == 1e9) pp("-1 -1\n"); else {
pp("%d %d\n", ans1, ans2);
}
}
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址