bzoj3508: 开灯

题目链接

题解

\(b[i]=a[i]\ xor\ a[i+1]\)

我们可以发现,修改只会改变\(b[l-1]\)\(b[r]\)

然后发现\(b[i]=1\)的点最多\(2*k\)

状压\(dp\)

Code

void bfs(int s) {
	memset(vis, 0, sizeof(vis));
	vis[s] = 1; q.push(make_pair(s, 0));
	while (!q.empty()) {
		int u = q.front().first, d = q.front().second; q.pop();
		if (b[u]) g[num[s]][num[u]] = d;
		for (int i = 1; i <= l; i++) {
			if (u + a[i] <= n && !vis[u + a[i]])
				vis[u + a[i]] = 1, q.push(make_pair(u + a[i], d + 1));
			if (u - a[i] >= 0 && !vis[u - a[i]])
				vis[u - a[i]] = 1, q.push(make_pair(u - a[i], d + 1));
		}
	}  
}
void solve() {
	n = gi<int>(), k = gi<int>(), l = gi<int>();
	memset(b, 0, sizeof(b)); tot = 0;
	for (int i = 1; i <= k; i++) b[gi<int>()] = 1;
	for (int i = 0; i <= n; i++)
		if (b[i] ^= b[i + 1])
			num[i] = tot++;
	for (int i = 1; i <= l; i++) a[i] = gi<int>();
	int lim = 1 << tot;
	memset(g, 0x3f, sizeof(g));
	memset(f, 0x3f, sizeof(f));
	for (int i = 0; i <= n; i++)
		if (b[i]) bfs(i);
	f[0] = 0;
	for (int i = 1, p; i < lim; i++) {
		p = 0; while (!(i & 1 << p)) p++;
		for (int j = p + 1; j < tot; j++)
			if (i & 1 << j)
				f[i] = min(f[i], f[i ^ 1 << p ^ 1 << j] + g[j][p]);
	}
	printf("%d\n", f[lim - 1] > inf ? -1 : f[lim - 1]);
	return ;
}

posted @ 2019-10-25 11:48  zzy2005  阅读(135)  评论(0编辑  收藏  举报