【题解】洛谷 P3943 星空【20201017 CSP 模拟赛】【差分 BFS 状压DP】

题目链接

题目链接

题意

\(n\) 个灯泡排成一排,其中 \(k\) 个是暗的。给定集合 \(S\),每次操作可以选择长度为 \(a\in S\) 的一段连续子区间,将区间内灯泡状态取反。问至少多少次操作后所有灯泡都变亮。\(n\leq 4\times 10^4\)\(k\leq 8\)\(|S|\leq 64\)

题解

首先差分一下,每次操作会在两个点取反,显然把两个点都取反成 \(1\) 是不优的,于是每次会把一个 \(1\) 换位置(若换到另一个 \(1\) 的位置则两个 \(1\) 就被消掉了)。先通过 BFS 找到每两个 \(1\) 之间的最短路。接着通过状压 DP 把所有 \(1\) 一对一对地消掉。

代码:

#include<bits/stdc++.h>
using namespace std;
int getint(){
	int ans=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		ans=ans*10+c-'0';
		c=getchar();
	}
	return ans*f;
}
#define ll long long
const int N=40010,M=140;
int f[1<<16];
bool q[N];int p[N],cnt=0;
int l[N];
int dis[N],d[M][M];
int n,k,m;
int main(){
	n=getint(),k=getint(),m=getint();
	for(int i=1;i<=k;i++){
		int x=getint();
		q[x]^=1; q[x-1]^=1;
	}
	for(int i=0;i<m;i++)l[i]=getint();
	for(int i=0;i<=n;i++)if(q[i])p[cnt++]=i;
	for(int i=0;i<cnt;i++){
		memset(dis,0x3f,sizeof(dis));
		dis[p[i]]=0;
		queue<int>q;
		q.push(p[i]);
		while(q.size()){
			int t=q.front();
			q.pop();
			for(int j=0;j<m;j++){
				int v=t+l[j];
				if(v<=n&&dis[v]>dis[t]+1){
					dis[v]=dis[t]+1;
					q.push(v);
				}
				v=t-l[j];
				if(v>=0&&dis[v]>dis[t]+1){
					dis[v]=dis[t]+1;
					q.push(v);
				}
			}
		}
		for(int j=0;j<cnt;j++)d[i][j]=dis[p[j]];
	}
	memset(f,0x3f,sizeof(f));
	f[0]=0;
	for(int i=0;i<(1<<cnt)-1;i++){
		int t=0;
		while((i>>t)&1)++t;
		for(int k=t+1;k<cnt;k++){
			if((i>>k)&1)continue;
			f[i|(1<<t)|(1<<k)]=min(f[i|(1<<t)|(1<<k)],f[i]+d[t][k]);
		}
	}
	cout<<f[(1<<cnt)-1];
}

posted @ 2020-10-17 15:12  破壁人五号  阅读(87)  评论(0编辑  收藏  举报