题意
长度为n的数组,上面有k个位置是1,现在有l种长度的连续全1串,要求用最少的次数将这个数组异或成全0的数组。n<=1E5,k<=10,l<=100。
思考
先将数组进行异或的差分。可以发现,现在异或上全1串只是将两个端点进行异或。
接下来求出将每对1变成0的最小代价,之后就是简单状压。
复杂度O(nkl+2^(2k))。
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=2E5+5; 4 const int inf=23333333; 5 int n,k,l; 6 int a[maxn],b[maxn],len[maxn],where[maxn],tot; 7 int dis[55][55]; 8 int f[maxn],ans[(1<<21)+5]; 9 bool vis[maxn]; 10 void bfs(int S,int num) 11 { 12 memset(vis,0,sizeof(vis)); 13 queue<int>Q; 14 Q.push(S); 15 vis[S]=1; 16 for(int i=1;i<=n;++i) 17 f[i]=inf; 18 f[S]=0; 19 while(!Q.empty()) 20 { 21 int u=Q.front(); 22 Q.pop(); 23 for(int i=1;i<=l;++i) 24 { 25 int v=u+len[i],nw=f[u]+1; 26 if(v>n) 27 continue; 28 if(nw<f[v]) 29 { 30 f[v]=nw; 31 if(!vis[v]) 32 Q.push(v),vis[v]=1; 33 } 34 } 35 for(int i=1;i<=l;++i) 36 { 37 int v=u-len[i],nw=f[u]+1; 38 if(v<1) 39 continue; 40 if(nw<f[v]) 41 { 42 f[v]=nw; 43 if(!vis[v]) 44 Q.push(v),vis[v]=1; 45 } 46 } 47 } 48 for(int i=1;i<=tot;++i) 49 dis[num][i]=f[where[i]]; 50 } 51 int main() 52 { 53 scanf("%d%d%d",&n,&k,&l); 54 for(int i=1;i<=k;++i) 55 { 56 int x; 57 scanf("%d",&x); 58 a[x]=1; 59 } 60 ++n; 61 for(int i=1;i<=l;++i) 62 scanf("%d",&len[i]); 63 for(int i=1;i<=n;++i) 64 { 65 b[i]=a[i]^a[i-1]; 66 if(b[i]) 67 where[++tot]=i; 68 } 69 for(int i=1;i<=tot;++i) 70 bfs(where[i],i); 71 for(int S=0;S<(1<<tot);++S) 72 ans[S]=inf; 73 ans[0]=0; 74 for(int S=0;S<(1<<tot);++S) 75 { 76 for(int i=0;i<tot;++i) 77 { 78 for(int j=i+1;j<tot;++j) 79 { 80 if((S&(1<<i))||(S&(1<<j))) 81 continue; 82 int nS=S|(1<<i)|(1<<j); 83 int nw=ans[S]+dis[i+1][j+1]; 84 ans[nS]=min(ans[nS],nw); 85 } 86 } 87 } 88 if(ans[(1<<tot)-1]==inf) 89 printf("-1\n"); 90 else 91 printf("%d\n",ans[(1<<tot)-1]); 92 return 0; 93 }