P7450 [THUSCH2017] 巧克力
巧克力
给定
n\times m 的网格以及两个参数a,c 。要求选出一个连通块,不包含
c=-1 的网格,且c 的种类数\geq k 。在此前提下最小化连通块大小,并在此前提下最小化
a 的中位数。
中位数直接套路的二分解决,不是本题的重点。看到
当
对更大的情况,考虑随机化,将所有颜色任意映射至
当最优解包含的
由于
实现上,这里的斯坦纳树将所有同色节点都近似的缩成了一个点,故初始化的时候有些特殊。
时间复杂度大概是
#include<bits/stdc++.h>
typedef long long LL;
#define rep(i, s, t) for(int i = (s); i <= (t); i ++)
#define per(i, s, t) for(int i = (s); i >= (t); i --)
#define Ede(i, u) for(int i = head[u]; i; i = e[i].nxt)
using namespace std;
const int N = 300;
const int INF = 0x3f3f3f3f;
int n, m, k, c[N][N], a[N][N], w[N][N], tt, col[N], to[N];
int f[N][N][1 << 6]; bool vis[N][N];
mt19937 myrand(20060814);
#define MP make_pair
typedef pair<int, int> PII;
queue<PII> q;
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};
int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
void spfa(int s) {
while(! q.empty()) {
PII now = q.front(); q.pop();
int x = now.first, y = now.second;
vis[x][y] = false;
rep(o, 0, 3) {
int tx = x + dx[o], ty = y + dy[o];
if(tx < 1 || tx > n || ty < 1 || ty > m || c[tx][ty] == - 1) continue;
if(f[x][y][s] + w[tx][ty] < f[tx][ty][s]) {
f[tx][ty][s] = f[x][y][s] + w[tx][ty];
if(! vis[tx][ty]) q.push(MP(tx, ty)), vis[tx][ty] = true;
}
}
}
}
int work() {
int ans = INF;
rep(Q, 1, 233) {
shuffle(col + 1, col + tt + 1, myrand);
rep(i, 1, tt) to[col[i]] = i % k;
rep(i, 1, n) rep(j, 1, m) {
rep(s, 0, (1 << k) - 1) f[i][j][s] = INF;
if(~ c[i][j]) f[i][j][1 << to[c[i][j]]] = w[i][j];
}
rep(s, 1, (1 << k) - 1) {
rep(i, 1, n) rep(j, 1, m) if(~ c[i][j]) {
for(int t = (s - 1) & s; t; t = (t - 1) & s)
f[i][j][s] = min(f[i][j][s], f[i][j][t] + f[i][j][s ^ t] - w[i][j]);
if(f[i][j][s] < 1e9) q.push(MP(i, j)), vis[i][j] = true;
}
spfa(s);
}
rep(i, 1, n) rep(j, 1, m) ans = min(ans, f[i][j][(1 << k) - 1]);
}
return ans;
}
int main() {
int T = read();
while(T --) {
tt = 0;
n = read(), m = read(), k = read();
rep(i, 1, n) rep(j, 1, m) c[i][j] = col[++ tt] = read();
rep(i, 1, n) rep(j, 1, m) a[i][j] = read(), w[i][j] = 1;
sort(col + 1, col + tt + 1);
tt = unique(col + 1, col + tt + 1) - (col + 1);
int rec = work();
if(rec == INF) {puts("-1 -1"); continue;}
int l = 0, r = 1e6;
while(l < r) {
int mid = (l + r) >> 1;
rep(i, 1, n) rep(j, 1, m) w[i][j] = ((a[i][j] <= mid) ? 9999 : 10001);
int now = work();
if(now <= rec * 10000) r = mid; else l = mid + 1;
}
printf("%d %d\n", rec, l);
}
return 0;
}