P r o b l e m \mathrm {Problem} Problem
LED屏是由一个庞大的点阵小灯泡组成的,一开始每个小灯泡都不发光。
每一行一共有N个小灯泡,依次标号为1~n。现在给定K个点,要求这K个点发光,其余点必须保持熄灭状态。
而这块LED屏的操作方式各种奇葩,一共有L种操作方法,第i种表示你能将任意长度恰为A_i的连续一段灯泡的状态取反(灭变亮,亮变灭)。
已知LED屏一共有m行,为了节省时间,请你算出每一行达到目标状态所需的最少操作次数
S o l u t i o n \mathrm{Solution} Solution
对于这道题,我们的主要思路就是讲区间取反转化为单点取反。
如果对于区间 [ l , r ] [l,r] [l,r]取反,我们可以维护差分数组 v v v,分别对 v l v_l vl和 v r + 1 v_{r+1} vr+1取反,再维护前缀和即可得到区间取反的操作,这样,我们就将区间操作转化为了单点操作。
那么显然对于初始序列的差分序列,你只需要读入 x x x以后将 x x x和 x + 1 x+1 x+1这两个位置分别做取反操作即可。
紧接着,我们要考虑如何将差分数组变成0.
我们发现如果我们要同时消掉两个数。例如,长度是 3 3 3和 5 5 5,两个1是位置 1 1 1和 9 9 9.
肯定是 1 1 1和 4 4 4消一次, 1 1 1变成 0 0 0, 4 4 4变成 1 1 1,再 4 4 4和 9 9 9消一次, 4 4 4变成 0 0 0, 9 9 9变成 0 0 0.因此两点间消掉的最少次数就是用给定的长度跳转的最短路径。
然后我们考虑状压, f [ s ] f[s] f[s]表示状态 S S S的灯已经熄灭的最少花费。我们可以任意找一盏开着的灯,在其它灯中找一个花费最少的决策来转移即可。
就像这样:
C o d e \mathrm{Code} Code
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int K = 22;
const int N = 200000;
int n, k, L;
int m = 0;
int a[K], v[N], len[N], w[K], used[N];
int dis[N], vis[N], f[1<<K], d[K][K];
priority_queue< pair<int,int> > q;
int read(void)
{
int s = 0, w = 0; char c = getchar();
while (c < '0' or c > '9') w |= c == '-', c = getchar();
while (c >= '0' and c <= '9') s = s*10+c-48, c = getchar();
return w ? -s : s;
}
void work(void)
{
n = read(), k = read(), L = read();
m = 0;
memset(d,30,sizeof d);
memset(v,0,sizeof v);
memset(used,0,sizeof used);
for (int i=1;i<=k;++i)
{
a[i] = read();
if (used[a[i]]) continue;
used[a[i]] = 1;
v[a[i]] ^= 1; v[a[i]+1] ^= 1;
}
for (int i=1;i<=L;++i) len[i] = read();
for (int i=1;i<=n+1;++i)
if (v[i]) w[m++] = i;
sort(w,w+m);
for (int i=0;i<m;++i)
{
memset(vis,0,sizeof vis);
memset(dis,30,sizeof dis);
while (q.size()) q.pop();
q.push(make_pair(0,w[i])), dis[w[i]] = 0;
while (q.size())
{
int x = q.top().second; q.pop();
if (vis[x]) continue; vis[x] = 1;
for (int j=1;j<=L;++j)
{
if (x-len[j] >= 1 && dis[x]+1 < dis[x-len[j]]) {
dis[x-len[j]] = dis[x] + 1;
q.push(make_pair(-dis[x-len[j]],x-len[j]));
}
if (x+len[j] <= n+1 && dis[x]+1 < dis[x+len[j]]) {
dis[x+len[j]] = dis[x] + 1;
q.push(make_pair(-dis[x+len[j]],x+len[j]));
}
}
}
for (int j=0;j<m;++j) d[i][j] = dis[w[j]];
}
memset(f,30,sizeof f);
f[0] = 0;
for (int i=1;i<1<<m;++i)
{
int t = 0;
while (((i >> t) & 1) == 0) t ++;
for (int j=t+1;j<m;++j)
f[i] = min(f[i],f[i^(1<<t)^(1<<j)]+d[t][j]);
}
if (f[(1<<m)-1] > 1e8) puts("-1");
else printf("%d\n", f[(1<<m)-1]);
}
int main(void)
{
int T = read();
while (T --) work();
return 0;
}