这个题……首先要记清楚亮着的灯是1,不亮的是0
然后一改改一排嘛所以肯定要差分的
因为是01串所以每个位置上只能是0或者1(废话嘛
如果构造一个差分数列分别等于相邻两位是否一样
那么一个数前面每一位的“一不一样”就可以表示这一位和第一位一不一样
只要知道第一位是0还是1
也就间接地知道了这一位是多少
这个一不一样可以通过异或操作来实现
前面每一位一不一样可以求个异或前缀和
两次不一样就一样了嘛
为了方便记录
我们令b[i]=a[i]^a[i+1]
a是输入的
然后增加一位b[0]
假装在最前面的小灯泡前面还有一个小灯泡
作为上面说的已经知道了的第一位
感觉上这一位是0还是1都无所谓
实际上如果是0的话很难处理
我太菜了定为0写不出来
姑且定为1吧
那么终止状态也就是差分数组全是0
然后把翻转操作对应到差分数组上
如果说翻转i~j吧(改变j-i+1个灯泡)
那么在差分数组上实际改变的值是i-1和j(间隔j-i+1)
它们都异或了1
为了达到全0串我们不会选择i-1和j都是0的异或
只会选择全为1或者一个为1一个为0的
可以发现前一种会消除两个1而后一种1只是挪了个位置
这里的思路其实有点像经典神题独木桥
一次消除两个的话……万一是奇数个1怎么办?
这时候再次为了方便起见
在所有小灯泡后面也添一个亮着的小灯泡
差分数组的范围为从0~n
其中第n项等于最后一个真实的小灯泡异或上人为添加的小灯泡
因为最前最后两个人为添加的小灯泡都是亮着的
所以差分数组里的1肯定是偶数个
继续
那么这样的消除和挪位置不是任意长度都可以的
只有两个点间隔距离正好等于操作长度时才可以(见上文括号
可以通过bfs处理出每个1可以挪到哪里,需要多少步
另外消除两个1当然可以看成把1挪到合适距离消除
然而更好的做法是看成一个1挪到另一个1的位置上(消消乐
整理一下,现在的题目已经转化成
一个01串,其中有偶数个1且部分1可以消耗一定的步数两两消除
因为1的个数很小,可以直接状压dp
压成一个数,在这个数的二进制表示上从左到右数第i位
表示初始位置在第i个的1有没有被消除
#include<bits/stdc++.h>
using namespace std;
struct node{
int to,nxt;
}road[3000000];
int cnt=0,head[70000],dis[30][70000],op[200],n,m,k;
int read()
{
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
void add(int u,int v)
{
road[++cnt].to=v;
road[cnt].nxt=head[u];
head[u]=cnt;
}
queue<int>q;
void bfs(int st,int stn)
{
q.push(st);
dis[stn][st]=0;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=1;i<=m;i++)
{
int lst=u-op[i],nxt=u+op[i];
if(lst>=0&&dis[stn][u]+1<dis[stn][lst])
{
q.push(lst);
dis[stn][lst]=dis[stn][u]+1;
}
if(nxt<=n&&dis[stn][u]+1<dis[stn][nxt])
{
q.push(nxt);
dis[stn][nxt]=dis[stn][u]+1;
}
}
}
}
int dp[4000000],pos[100],x[100],a[70000],b[70000],pcnt=0;
int solve()
{
int st=(1<<pcnt)-1;
dp[st]=0;
for(int i=st-1;i>=0;i--)
for(int j=0;j<pcnt;j++)
if(!((1<<j)&i)) //注意这个条件
for(int jj=0;jj<j;jj++)
if(!((1<<jj)&i)) dp[i]=min(dp[i],dp[i|(1<<j)|(1<<jj)]+dis[j+1][pos[jj+1]]);
return dp[0];
}
int main()
{
n=read(),k=read(),m=read();
for(int i=0;i<=n+1;i++) a[i]=1;
for(int i=1;i<=k;i++)
{
x[i]=read();
a[x[i]]=0;
}
for(int i=0;i<=n;i++)
{
b[i]=a[i]^a[i+1];
if(b[i]) pos[++pcnt]=i;
}
for(int i=1;i<=m;i++) op[i]=read();
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
if(i+op[j]+1<=n)
{
add(i,i+op[j]);
add(i+op[j],i);
}
}
memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=pcnt;i++)
bfs(pos[i],i);//这个东西的复杂度是O(nm)的
memset(dp,0x3f,sizeof(dp));
int ans=solve();
if(ans>=0x3f3f3f3f)cout<<"-1";
else cout<<ans;
return 0;
}