洛谷3943 星空

题目描述

逃不掉的那一天还是来了,小 F 看着夜空发呆。
天上空荡荡的,没有一颗星星——大概是因为天上吹不散的乌云吧。
心里吹不散的乌云,就让它在那里吧,反正也没有机会去改变什么了。
小 C 拿来了一长串星型小灯泡,假装是星星,递给小 F,想让小 F 开心一点。不过,有 着强迫症的小 F 发现,这串一共 n 个灯泡的灯泡串上有 k 个灯泡没有被点亮。小 F 决定 和小 C 一起把这个灯泡串全部点亮。
不过,也许是因为过于笨拙,小 F 只能将其中连续一段的灯泡状态给翻转——点亮暗灯 泡,熄灭亮灯泡。经过摸索,小 F 发现他一共能够翻转 m 种长度的灯泡段中灯泡的状态。
小 C 和小 F 最终花了很长很长很长很长很长很长的时间把所有灯泡给全部点亮了。他 们想知道他们是不是蠢了,因此他们找到了你,让你帮忙算算:在最优的情况下,至少需要 几次操作才能把整个灯泡串给点亮?

solution

这题第二个转化很巧妙,首先第一个比较简单的转化:转变为差分数组,然后每一次区间翻转就变成了单点翻转,并且我们发现只需要把所有的1变为0就可以达到要求了,但是这里有一个重要的细节,差分数组需要多加一位,因为修改 \([l,r+1]\),时可能会改出去,样例就是一个提醒.然后就是第二个转化,每一次我们翻转:如果翻转的两个位置都为1,那么就相当于消除,如果一个0一个1相当于移动,所以一对1可以看做移动了很多步然后消除,所以可以预处理出任意点对消除的步数,bfs处理即可,另外就是注意到1的个数不超过 \(16\),所以用预处理出来的东西,状压DP,类似于noip愤怒的小鸟了

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=40005;
int n,Q,m,mov[N],st[N],top=0,id[N],DFN=0;bool a[N];
struct node{
	int x,y;
	node(){}
	node(int _x,int _y){x=_x;y=_y;}
};
queue<node>q;
struct way{
	int x,w;
}e[N];
int tot=0;bool vis[N];
inline void bfs(int S){
	int x,u,step;
	q.push(node(S,0));
	top=0;vis[S]=1;st[++top]=S;
	while(!q.empty()){
		x=q.front().x;step=q.front().y;
		q.pop();
		for(RG int i=1;i<=m;i++){
			for(int k=1;k<=2;k++){
				if(k==1)u=x+mov[i];
				else u=x-mov[i];
				if(u>n || vis[u] || u<1)continue;
				vis[u]=1;st[++top]=u;
				if(a[u]==1){
					e[++tot].x=id[S]|id[u];
					e[tot].w=step+1;
					continue;
				}
				q.push(node(u,step+1));
			}
		}
	}
	while(top)vis[st[top]]=0,top--;
}

int f[1<<16];bool b[N];
void work()
{
	int x;
	scanf("%d%d%d",&n,&Q,&m);
	n++;
	for(int i=1;i<=Q;i++){
		scanf("%d",&x);
		b[x]=1;
	}
	for(int i=1;i<=n;i++)a[i]=b[i]^b[i-1];
	for(int i=1;i<=m;i++)scanf("%d",&mov[i]);
	for(int i=1;i<=n;i++)if(a[i])id[i]=(1<<DFN),DFN++;
	for(int i=1;i<=n;i++)
		if(a[i])bfs(i);
	
	memset(f,127/3,sizeof(f));
	f[0]=0;
	int lim=(1<<DFN)-1;
	for(RG int j=0;j<=lim;j++){
		for(RG int i=1;i<=tot;i++){
			if(j&e[i].x)continue;
			f[j|e[i].x]=Min(f[j|e[i].x],f[j]+e[i].w);
		}
	}
	printf("%d\n",f[lim]);
}

int main()
{
	work();
	return 0;
}

posted @ 2017-11-08 14:30  PIPIBoss  阅读(270)  评论(0编辑  收藏  举报