题目
hzwer 有一把密码锁,由 N 个开关组成。一开始的时候,所有开关都是关上的。当且仅 当开关 x1,x2,x3,…xk 为开,其他开关为关时,密码锁才会打开。
他可以进行 M 种的操作,每种操作有一个 size[i],表示,假如他选择了第 i 种的操作的话,他可以任意选择连续的 size[i]个格子,把它们全部取反。(注意,由于黄金大神非常的神,所以操作次数可以无限><).
本来这是一个无关紧要的问题,但是,黄金大神不小心他的钱丢进去了,没有的钱他哪里能逃过被 chenzeyu97 NTR 的命运?>< .于是,他为了虐爆 czy,也为了去泡更多的妹子,决定打开这把锁。但是他那么神的人根本不屑这种水题。于是,他找到了你。你的任务很简单,求出最少需要多少步才能打开密码锁,或者如果无解的话,请输出-1。
输入
第 1 行,三个正整数 N,K,M,如题目所述。
第 2 行,K 个正整数,表示开关 x1,x2,x3…xk 必须为开,保证 x 两两不同。第三行,M 个正整数,表示 size[i],size[]可能有重复元素。
输出
输出答案,无解输出-1
样例输入 Copy
10 8 2
1 2 3 5 6 7 8 9
3 5
样例输出 Copy
2
提示
样例输入2:
3 2 1
1 2
3
样例输出2:
-1
对于 50%的数据,1≤N≤20,1≤k≤5,1≤m≤3;
对于另外 20%的数据,1≤N≤10000,1≤k≤5,1≤m≤30;
对于 100%的数据,1≤N≤10000,1≤k≤10,1≤m≤100;
前言:
和网上的思路差不多。(或者说就是网上的思路)
想法:
50pts
很容易想到用二进制来表示灯的状态。
由于 n < = 20 n<=20 n<=20,所以 2 20 2^{20} 220的状态完全可行。
- 状压dp
f [ s ] f[s] f[s]表示灯的状态为 s s s的时候用的最少步数,转移就枚举用的是哪种操作,适用到哪个区间。 O ( 2 n n m ) O(2^nnm) O(2nnm) 勉强。
- bfs
建出 2 20 2^{20} 220个点,在这张图上瞎走走。。复杂度差不多吧。
100pts
区间取反=区间 x o r 1 xor\ 1 xor 1=区间 m o d 2 mod\ 2 mod 2意义下加法
考虑把操作倒过来不影响答案。
把原来的 k k k个关键点在原来的图上差分,变成 2 k 2k 2k个点。
现在的操作就是同时倒置两个点,借用其他人的说法,把原始的点看作石子。
我们的任务是消石子,消成全 0 0 0。
如果两个点以前都没有石子,不操作。因为操作会增加偶数个石子,一定不利。
如果一个点有石子,一个点没有。相当于交换石子的位置。
如果两个点都有石子,相当于抵消了。(兑子)
所以,可以 b f s bfs bfs预处理出 d i s [ i ] [ j ] dis[i][j] dis[i][j]表示 i i i这个位置的石子到 j j j位置的距离。
一般图最小权最大匹配
- 带花树
不会
- 状压dp
f [ s ] f[s] f[s]表示点的状态为 s s s的时候的最小步数。总点数为 t o t tot tot的话,一开始 f [ ( 1 < < t o t ) − 1 ] = 0 f[(1<<tot)-1]=0 f[(1<<tot)−1]=0
转移就枚举当前位置最小的一个石子,枚举它是交换到另外一个空地方还是与其他一个棋子兑掉了。 都可以用异或很简单地实现。
O ( k n m + 2 2 k k ) O(knm + 2^{2k}k) O(knm+22kk)
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int N = 2e4 + 5, K = 24, M = 105, INF = 0x3f3f3f3f;
int f[1<<K];
struct poi {
int id, x;
}c[K];
int siz[M], a[K], exi[N], val[N], vis[N], dis[K][K], dist[N], tot;
int n, k, m;
queue<int>q;
inline void bfs(int s) {
while (!q.empty())q.pop();
memset(vis, 0, sizeof vis);
memset(dist, 0x3f, sizeof dist);
q.push(c[s].x); vis[ c[s].x ] = 1; dist[ c[s].x ] = 0;
while (!q.empty()) {
int cur = q.front(); q.pop();
for (int i = 1; i <= m; ++i) {
int nxtx = cur - siz[i];
if (nxtx > 0 && !vis[nxtx]) {
vis[nxtx] = 1; dist[nxtx] = dist[cur] + 1;
q.push(nxtx);
}
nxtx = cur + siz[i];
if (nxtx <= n + 1 && !vis[nxtx]) {
vis[nxtx] = 1; dist[nxtx] = dist[cur] + 1;
q.push(nxtx);
}
}
}
for (int i = 1; i <= tot; ++i) dis[s][i] = dist[ c[i].x ];
}
int main() {
scanf("%d %d %d", &n, &k, &m);
for (int i = 1; i <= k; ++i) {
scanf("%d", &a[i]);
exi[ a[i] ] = 1;
}
for (int i = 1; i <= k; ++i) {
val[ a[i] ] = 1 - exi[ a[i] - 1];
val[ a[i] + 1] = exi[ a[i] + 1] - 1;
}
for (int i = 1; i <= n + 1; ++i) {
if (val[i] != 0) {
c[++tot].id = tot;
c[tot].x = i;
}
}
for (int i = 1; i <= m; ++i)scanf("%d", &siz[i]);
for (int i = 1; i <= tot; ++i) bfs(i);
memset(f, 0x3f, sizeof f);
f[(1<<tot) - 1] = 0;
for (int i = (1 << tot) - 1; i >= 0; --i) {
int pos = 0;
for (int j = 1; j <= tot; ++j) {
if ((i >> (j - 1)) & 1) {
pos = j;
break;
}
}
if (pos) {
for (int j = 1; j <= tot; ++j) {
int nxt = i ^ (1 << (j - 1)) ^ (1 << (pos - 1));
f[nxt] = min(f[nxt], f[i] + dis[pos][j]);
}
}
}
if (f[0] != INF) printf("%d\n", f[0]);
else puts("-1");
return 0;
}