P3943 星空 题解

题目链接

星空

分析

题目中的区间反转,相当于区间异或 \(1\)
对于区间操作,我们可以想到差分
对本题来说,我们可以做一次异或差分
也就是 \(c[i]=a[i]\) 异或 \(a[i-1]\)
这样以后,对于 \([i,i+w]\) 的操作可以转化为对差分数组 \(c[i]\)\(c[i+w+1]\) 取反
每次操作,我们一定会贪心的让其中一个点为 \(1\) ,只有当另一个点也为 \(1\) 时, \(1\) 的个数才会减少
所以可以用 \(bfs\) 预处理出消除任意两个 \(1\) 的最少花费
考虑到 差分后 \(1\) 的个数最多只有 \(16\) 个 (\(2*8\)) 可以想到状压 \(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 @ 2023-11-16 22:30  Idtwtei  阅读(15)  评论(0编辑  收藏  举报