P3943 星空

P3943

思维好题

接下来, 您将看到 :

  • 差分(??!)
  • bfs
  • 状压dp

第一波操作(差分):

d[i] = a[i] ^ a[i-1]

例子: 10011100 (0表示不亮, 1为亮了) 它的"差分"数组为 11010010

吃瓜群众: 为什么要这么表示

let's 模拟 it

原状态 : 10011100 -> 11010010 将第四盏到第六盏搞一下 10000000 -> 11000000

fa♂现了没有, 差分数组位置4, 7改变, 因为异或使4到6同时改变, 他们相邻两个异或值不会改变

而3和4, 6和7中有一个值改变, 那么他们异或值也会改变

同样的, 如果操作第四盏到第七盏, 发现差分数组位置4的1移到了位置8(汝可模拟得知)

所以我们要求出所有的1,移动最少几次和另一个1配对

第二波操作(bfs):

预处理出每一个1到其他1的距离, bfs就行啦

第三波操作(状压dp):

设计状态S, f[S]表示消除点集S至少要操作多少次

f[S] = min(f[S], f[S-'两个点'] + 两点相消要走多少次);

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 40050;
int b[70], n, m, k;
int a[50], f[N];
int pos[50], g[1005000];
inline int to(int x) {
	return 1 << x;
}
queue<int> q;
int main() {
	cin >> n >> k >> m;
	for (int i = 1;i <= k; i++) 
		cin >> a[i];
	sort(a + 1,a + k + 1);
	int cnt = 1;
	pos[1] = a[1];
	for (int i = 2;i <= k; i++) 
	if (a[i] != a[i-1] + 1) {
		pos[++cnt] = a[i-1]+1;
		pos[++cnt] = a[i];
	}
	pos[++cnt] = a[k]+1;
	for (int i = 1;i <= m; i++) cin >> b[i];
	memset(f, 0x3f, sizeof(f));
	f[0] = 0;
	q.push(0);
	while (q.size()) {
		int x = q.front(); q.pop();
		for (int i = 1;i <= m; i++) {
			int y = x + b[i];
			if (y <= n && f[y] > n) {
				f[y] = f[x] + 1;
				q.push(y);
			}
			y = x - b[i];
			if (y > 0 && f[y] > n) {
				f[y] = f[x] + 1;
				q.push(y);
			}
		}
	}		
	memset(g, 0x3f, sizeof(g));
	g[0] = 0;
	for (int i = 1;i < 1 << cnt; i++) {
		int tmp = 0;
		for (int j = 1;j <= cnt; j++) 
			if (i & to(j-1)) tmp++;
		if (tmp & 1) continue;
		for (int j = 1;j <= cnt; j++) 
			if (i & to(j-1))
			for (int k = j + 1;k <= cnt; k++) 
				if (i & to(k-1)) 
					g[i] = min(g[i], g[i^to(k-1)^to(j-1)] + f[pos[k]-pos[j]]);
	}
	cout << g[(1 << cnt) - 1] << endl;
	return 0;
}
posted @ 2019-10-05 23:08  Hs-black  阅读(136)  评论(0编辑  收藏  举报