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
#include
#include
#include
#include
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 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;
}