洛谷P2962 位运算,meet in middle

6 篇文章 0 订阅
5 篇文章 1 订阅
题目链接

https://www.luogu.com.cn/problem/P2962

题意

给出n点m边无向图,每一个节点都是开关,触发后会将它自己和所有直接相连节点状态改变(0->1,1->0)。初始全部状态为0,问最少几步全1。n<35

思路

首先易知任意一个节点最多摁下1次,因为摁下两次相当于没有操作

最朴素的方法就是搜索,我们利用二进制数来模拟点开关状态,不用常规的存图方法,我们用数组g记录节点x触发后可以直接改变的点。从1-n枚举每个点开关状态,触发新的点后的状态转移可以用二进制异或来表示,复杂度为O(pow(2,n)),35的数据,几乎过不了。

下面介绍折半搜索,Meet in the middle。它用来处理数据较小,但还不能直接暴力的问题。折半搜索将问题划分为两段,分别搜索,最后合并答案。

应用到本题,我们使用map处理步数和状态的映射,将点分为两个集合,分别搜索,当搜出的状态互补时(异或为全1串),即可更新答案,复杂度可以打个根号了,对于指数级算法来说,降低不少。

代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=55;
	const int inf=0x3f3f3f3f;
	int n,m,k;
    int half;
	struct custom_hash {static uint64_t splitmix64(uint64_t x) {x += 0x9e3779b97f4a7c15;x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;x = (x ^ (x >> 27)) * 0x94d049bb133111eb;return x ^ (x >> 31);}size_t operator()(uint64_t x) const {static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();return splitmix64(x + FIXED_RANDOM);}};
	unordered_map<int,int,custom_hash>mp;
    int over;
    int ans=inf;
    int g[maxn];
    void dfs1(int x,int cur,int dep){
        if(!mp[cur]) mp[cur]=dep;
        else   mp[cur]=min(mp[cur],dep);
        if(over==cur){
            ans=min(ans,dep);
            //return ;
        }
        if(x==half+1)   return ;
        dfs1(x+1,cur,dep);
        dfs1(x+1,cur^g[x],dep+1);
    }
    void dfs2(int x,int cur,int dep){
        if(!mp[cur]) mp[cur]=dep;
        else   mp[cur]=min(mp[cur],dep);
        if(mp[over^cur]||over==cur){
            ans=min(ans,dep+mp[over^cur]);
            //return ;
        }
        if(x==n+1)   return ;
        dfs2(x+1,cur,dep);
        dfs2(x+1,cur^g[x],dep+1);
    }
    signed main(){
		IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		cin>>n>>m;
        for(int i=1;i<=m;i++){
            int u,v;
            cin>>u>>v;
            g[u]^=1<<v;
            g[v]^=1<<u;
        }
        for(int i=1;i<=n;i++){
            over^=1<<i;
            g[i]^=1<<i;
        }
        half=n/2;
        dfs1(1,0,0);
        dfs2(half+1,0,0);
        cout<<ans<<endl;
	} 
						
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值