题意

长度为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 }
View Code

 

 posted on 2019-07-08 14:50  GreenDuck  阅读(161)  评论(0编辑  收藏  举报