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
", f[lim - 1] > inf ? -1 : f[lim - 1]);
	return ;
}

原文地址:https://www.cnblogs.com/zzy2005/p/11737221.html