[USACO09NOV] Lights G

假期集训Day2 T6(一道绝佳的双向DFS题)

题面

[题目描述]

牛棚里一共有 N 盏灯,电灯间有 M 条电线,其中第 i 条电线连接了第 A i 和 B i 盏灯,A i ̸= B i ,也不会有多条电线重复连接同一组灯。每盏灯上有个开关,开关会改变灯的状态——把不亮的灯点亮,或亮的灯变成不亮。不仅如此,由于电线的作用,按下某盏灯的开关后,和这盏灯直接相连的其他电 灯也会随之改变状态。 假设刚开始的时候,所有电灯都是关着的,请问按下哪些灯的开关,才能把所有的灯都点亮?输出按下开关的最少次数,保证解法一定存在。

输入

• 第一行:两个整数 N 和 M,1 ≤ N ≤ 35, 1 ≤ M ≤ 595

• 第二行到第 M + 1 行:第 i + 1 行有两个整数 A i 和 B i ,1 ≤ A i ,B i ≤ N

输出

单个整数:表示最少按下的开关次数

样例输入

5 6
1 2
1 3
4 2
3 4
2 5
5 3

样例输出

3

样例解释

按下第一盏灯,第四盏灯,第五盏灯的开关

解析

双 向 D F S

考虑用2进制表示每一种状态,第一盏灯为1<<1,第二盏灯为1<<2,第i盏灯为1<<i,这样我们可以表示出任何一种状态,用map储存每一种状态,当搜索到一种状态使得他和之前的某一种状态相加=灯全部亮的状态,就输出两者的步数和。特别注意,一个状态可能被多种方式搜索到,所以在用map储存的时候要取一个最小值,否则就会WA28-78分不等

#include <bits/stdc++.h>
using namespace std;
map<long long,long long> maps;
map<long long,bool> spam;
long long n,m,a[40][40],x,y,endd,s[40];
long long pow2(long long x){
	long long res=1;
	for(long long i=1;i<=x;i++) res*=2;
	return res;
}
void dfs(long long x,long long y,long long z){
	if(maps[y]==0) maps[y]=INT_MAX;
    maps[y]=min(maps[y],z);
	spam[y]=true;
	if(x>17) return;
	if(y==endd) { 
		cout<<maps[y];
		exit(0);
	}
	if(x>n) return;
	long long yy=y;
	for(long long i=1;i<=n;i++){ 
		if(a[x][i]==1||x==i){
		if((yy>>i)&(1ll)) yy-=pow2(i);
		else yy+=pow2(i);
		}
	} 
	dfs(x+1,y,z);  
	dfs(x+1,yy,z+1);  
}
void dfs2(long long x,long long y,long long z){
	if(maps[y]==0) maps[y]=INT_MAX;
    maps[y]=min(maps[y],z);
	if(x>36) return;
	if(spam[endd-y]!=0) {
		cout<<maps[y]+maps[endd-y];
		exit(0);
	}
	if(x>n) return;
	long long yy=y;
	for(long long i=1;i<=n;i++){ 
		if(a[x][i]==1||x==i)
		{
		if((yy>>i)&(1ll)) yy-=pow2(i);
		else yy+=pow2(i);
		}
	} 
	dfs2(x+1,y,z);  
	dfs2(x+1,yy,z+1);
}
int main(){
    scanf("%d%d",&n,&m);
    for(long long i=1;i<=m;i++){
    	scanf("%d%d",&x,&y);
    	a[x][y]=1,a[y][x]=1;
    }
    endd=pow2(n+1)-2;
    dfs(1,0,0);
    dfs2(18,0,0);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值