zoukankan      html  css  js  c++  java
  • 洛谷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
    ",dp[lim]);
    	return 0;
    }
    
  • 相关阅读:
    在线考试————随机出题
    HTTP协议
    团队
    做作业
    图书馆管理说明书性能
    关于敏捷开发的学习
    运行环境
    图书馆管理系统说明书
    性能(2)
    作业
  • 原文地址:https://www.cnblogs.com/toot-wjh/p/11336835.html
Copyright © 2011-2022 走看看