洛谷P3943 星空

题面:

题解:

  很好的思维题,考察数学抽象和问题转化能力。

  考虑到区间最长为$40000$,若暴力翻转,一次复杂度为$O(n)$,显然不可承受,考虑将区间操作转化为单点操作,所以我们可想到差分,因为一次翻转为取$xor$,所以我们定义差分为$b[i]=a[i]\ xor\ a[i+1]$。

  差分后数组变为一个$01$串,包含不超过$2k$个零。

  问题转化(转化一)为:给定一个$01$串,有不超过$2k$个零,每次操作时,从$m$种距离中选一个,把序列上相距为该距离的两个数取反,问使整个串为$0$的最少操作数。

  观察该问题,我们可能会有两种操作,

    1.一个$1$,一个$0$,相当于移动。

    2.两个$0$,相当于消去。

  这样,我们可以继续把问题转化(转化二)为:一张图,有$2k$个物品在不同起点,每次操作时,从$m$种距离中选一个,把该物品移动,当两物品相遇时,它们消去,求最少操作数。

  我们发现,图上一共有$n$个节点,每个点有$m$条边,于是我们可以用$spfa$预处理出两两间的最短路,复杂度为$O(nmk)$。

  考虑继续转化(转化三)为:2k个物品,选择其中两个可以消去,分别有不同的代价。

  显然可用状压处理,但注意要写复杂度$O(k*2^{2k})$的,防止被卡。附上大佬博客。

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
#define R register
#define ll long long
inline ll read(){
	ll aa=0;R int bb=1;char cc=getchar();
	while(cc<'0'||cc>'9')
		{if(cc=='-')bb=-1;cc=getchar();}
	while(cc>='0'&&cc<='9')
		{aa=(aa<<1)+(aa<<3)+(cc^48);cc=getchar();}
	return aa*bb;
}
const int N=40003;
const int M=18;
int n,k,m,a[N],b[N],tt,pos[M],dp[1<<M];
int ds[N],dis[M][M];
queue<int>qi;
inline void spfa(int st)
{
	memset(ds,0,sizeof(ds));
	int x=pos[st];ds[x]=1;qi.push(x);
	while(!qi.empty()){
		int u=qi.front();qi.pop();
		for(R int i=1;i<=m;++i){
			if(u+b[i]<=n&&!ds[u+b[i]]){
				ds[u+b[i]]=ds[u]+1;
				qi.push(u+b[i]);
			}
			if(u-b[i]>=0&&!ds[u-b[i]]){
				ds[u-b[i]]=ds[u]+1;
				qi.push(u-b[i]);
			}
		}
	}
	for(R int i=1;i<=tt;++i)dis[st][i]=ds[pos[i]]-1;
}
int main()
{
	n=read();k=read();m=read();
	for(R int i=1,x;i<=k;++i){x=read();a[x]^=1;}
	for(R int i=0;i<=n;++i){
		a[i]^=a[i+1];
		if(a[i])pos[++tt]=i;
	}
	for(R int i=1;i<=m;++i)b[i]=read();
	for(R int i=1;i<=tt;++i)spfa(i);
	const int lim=(1<<tt)-1;
	memset(dp,0x3f,sizeof(dp)); dp[0]=0;
	for(R int i=0;i<lim;++i)
		for(R int j=1;j<=tt;++j){
			if((1<<(j-1))&i)continue;
			for(R int q=j+1,x;q<=tt;++q){
				if(((1<<(q-1))&i)||dis[j][q]==-1)continue;
				x=(i|(1<<(q-1))|(1<<(j-1)));
				dp[x]=min(dp[x],dp[i]+dis[j][q]);
			}
			break;//保证复杂度,只需处理第一个位置,防止重复。
		}
	printf("%d\n",dp[lim]);
	return 0;
}
posted @ 2019-08-11 21:35  Toot_Holmes  阅读(116)  评论(0编辑  收藏  举报