P3943 星空 题解

题目链接

星空

分析

题目中的区间反转,相当于区间异或 1
对于区间操作,我们可以想到差分
对本题来说,我们可以做一次异或差分
也就是 c[i]=a[i] 异或 a[i1]
这样以后,对于 [i,i+w] 的操作可以转化为对差分数组 c[i]c[i+w+1] 取反
每次操作,我们一定会贪心的让其中一个点为 1 ,只有当另一个点也为 1 时, 1 的个数才会减少
所以可以用 bfs 预处理出消除任意两个 1 的最少花费
考虑到 差分后 1 的个数最多只有 16 个 (28) 可以想到状压 DP
然后就做完了

代码

#include<bits/stdc++.h>
using namespace std;
const int N=500004,INF=0x3f3f3f3f;
int n,k,m,ma=0;
int ta[N];
int a[40],tot=-1;//记录为0的灯的位置
int mov[200];
int f[1<<22];
int cost[200][200];
queue<int>q;
int dis[N];
bool vis[N];
void bfs(int x){
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
q.push(a[x]);vis[a[x]]=1;dis[a[x]]=0;
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=1;i<=m;i++){
if(u+mov[i]<=n&&!vis[u+mov[i]]){
dis[u+mov[i]]=dis[u]+1;
vis[u+mov[i]]=1;
q.push(u+mov[i]);
}
if(u-mov[i]>=1&&!vis[u-mov[i]]){
dis[u-mov[i]]=dis[u]+1;
vis[u-mov[i]]=1;
q.push(u-mov[i]);
}
}
}
for(int i=0;i<=tot;i++) cost[x][i]=dis[a[i]];
}
int main(){
scanf("%d %d %d", &n, &k, &m);n++;
for(int i=1;i<=k;i++){int x;scanf("%d", &x);ta[x]=1;}
for(int i=1;i<=m;i++){scanf("%d", &mov[i]);ma=max(ma,mov[i]);}
for(int i=1;i<=n;i++) if(ta[i]!=ta[i-1]) a[++tot]=i;//差分
for(int i=0;i<=tot;i++) bfs(i);
int lim=(1<<(tot+1))-1;//状压DP
for(int i=0;i<lim;i++) f[i]=INF;
for(int i=lim-1;i>=0;i--){
for(int p1=0;p1<=tot;p1++){
if((i>>p1)&1) continue;
for(int p2=p1+1;p2<=tot;p2++){
if((i>>p2)&1) continue;
f[i]=min(f[i],f[i^(1<<p1)^(1<<p2)]+cost[p1][p2]);
}
}
}
// if(f[0]==INF) cout<<-1<<endl;
else printf("%d\n", f[0]);
return 0;
}
posted @   Idtwtei  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示