f[S] 表示将差分序列的下标集合 S 进行取反,其他位置不受影响的最小步数( S 中仅包含不超过 2k 个位置)
显然 f[∅]=0
(1)枚举 i,j∈S,i̸=j ,表示 i 和 j 位置一起被取反,其他位置不受影响
f[S]=min(f[S],f[S−i−j]+g[i][j])
(2)枚举 i∈S ,表示 i 借助位置 n+1 实现取反,其他位置不受影响
f[S]=min(f[S],f[S−i]+h[i])
转移时可以假定 i 是 S 内的第一个元素(因为 S 内所有的位置都必须被取反)
最后答案显然为 f[all] , all 为最终状态差分 01 序列上 1 的位置集合
复杂度 O(knl+22k×k)
Code
#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>inlineintread(){int res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);return bo ?~res +1: res;}template<classT>inline T Min(const T &a,const T &b){return a < b ? a : b;}constint N =1e4+5, E =25, M =205, C =(1<<20)+5, INF =0x3f3f3f3f;int n, k, m, arr[N], a[M], tot, w[E], g[E][E], dis[N], len, que[N],
f[C], Cm, dn[C];voidbfs(int S){memset(dis, INF,sizeof(dis));
dis[que[len =1]= S]=0;for(int i =1; i <= len; i++){int u = que[i];for(int j =1; j <= m; j++){int v = u + a[j];if(v <1|| v > n +1|| dis[v]< INF)continue;
dis[que[++len]= v]= dis[u]+1;}}}intmain(){
n =read(); k =read(); m =read();for(int i =1; i <= k; i++) arr[read()]=1;for(int i = n; i; i--) arr[i]^= arr[i -1];for(int i =1; i <= m; i++)
a[i]=read(), a[i + m]=-a[i];
m <<=1;for(int i =1; i <= n; i++)if(arr[i]) w[++tot]= i;for(int i =1; i <= tot; i++){bfs(w[i]);for(int j =1; j <= tot; j++)
g[i][j]= dis[w[j]];}bfs(n +1);memset(f, INF,sizeof(f));
f[0]=0;
Cm =1<< tot;for(int i =1; i <= tot; i++) dn[1<< i -1]= i;for(int S =1; S < Cm; S++){int id = dn[S &-S], T = S ^(S &-S);
f[S]=Min(f[S], f[T]+ dis[w[id]]);for(int i =1; i <= tot; i++)if((T >> i -1)&1)
f[S]=Min(f[S], f[T ^(1<< i -1)]+ g[id][i]);}printf("%d\n", f[Cm -1]== INF ?-1: f[Cm -1]);return0;}