「THUSCH 2017」巧克力(斯坦纳树dp+随机化+中位数二分)

https://loj.ac/problem/2977

暴力:状态记录当前选了哪些颜色的点,用斯坦纳树去转移,应该能过个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);
		}
	}
}
posted @ 2020-04-05 21:01  Cold_Chair  阅读(175)  评论(0编辑  收藏  举报