题意
(n) 个灯泡排成一排,其中 (k) 个是暗的。给定集合 (S),每次操作可以选择长度为 (ain S) 的一段连续子区间,将区间内灯泡状态取反。问至少多少次操作后所有灯泡都变亮。(nleq 4 imes 10^4),(kleq 8),(|S|leq 64)。
题解
首先差分一下,每次操作会在两个点取反,显然把两个点都取反成 (1) 是不优的,于是每次会把一个 (1) 换位置(若换到另一个 (1) 的位置则两个 (1) 就被消掉了)。先通过 BFS 找到每两个 (1) 之间的最短路。接着通过状压 DP 把所有 (1) 一对一对地消掉。
代码:
#include<bits/stdc++.h>
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
#define ll long long
const int N=40010,M=140;
int f[1<<16];
bool q[N];int p[N],cnt=0;
int l[N];
int dis[N],d[M][M];
int n,k,m;
int main(){
n=getint(),k=getint(),m=getint();
for(int i=1;i<=k;i++){
int x=getint();
q[x]^=1; q[x-1]^=1;
}
for(int i=0;i<m;i++)l[i]=getint();
for(int i=0;i<=n;i++)if(q[i])p[cnt++]=i;
for(int i=0;i<cnt;i++){
memset(dis,0x3f,sizeof(dis));
dis[p[i]]=0;
queue<int>q;
q.push(p[i]);
while(q.size()){
int t=q.front();
q.pop();
for(int j=0;j<m;j++){
int v=t+l[j];
if(v<=n&&dis[v]>dis[t]+1){
dis[v]=dis[t]+1;
q.push(v);
}
v=t-l[j];
if(v>=0&&dis[v]>dis[t]+1){
dis[v]=dis[t]+1;
q.push(v);
}
}
}
for(int j=0;j<cnt;j++)d[i][j]=dis[p[j]];
}
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=0;i<(1<<cnt)-1;i++){
int t=0;
while((i>>t)&1)++t;
for(int k=t+1;k<cnt;k++){
if((i>>k)&1)continue;
f[i|(1<<t)|(1<<k)]=min(f[i|(1<<t)|(1<<k)],f[i]+d[t][k]);
}
}
cout<<f[(1<<cnt)-1];
}