第十二届蓝桥杯JavaB组省赛-回路计数

该博客主要探讨了一种利用状态压缩和记忆化搜索算法解决数学问题的方法,具体是寻找在特定条件下教学楼之间的哈密尔顿回路。博主分享了初始尝试使用深度搜索导致超时的问题,然后介绍了如何优化为状态压缩和记忆化搜索,并给出了详细的代码实现。通过这种优化,程序的运行时间从原来的超过十分钟降低到了1.5秒,成功解决了问题。
摘要由CSDN通过智能技术生成

题目

蓝桥学院由 21 栋教学楼组成,教学楼编号 1 到 21。

对于两栋教学楼 a 和 b,当 a 和 b 互质时,a 和 b 之间有一条走廊直接相连,两个方向皆可通行,否则没有直接连接的走廊。

小蓝现在在第一栋教学楼,他想要访问每栋教学楼正好一次,最终回到第一栋教学楼(即走一条哈密尔顿回路),

请问他有多少种不同的访问方案?两个访问方案不同是指存在某个 i,小蓝在两个访问方法中访问完教学楼 i 后访问了不同的教学楼。

提示:建议使用计算机编程解决问题。

答案:881012367360

思路:状态压缩+记忆化搜索

记得一开始没多想,直接写了个深度搜索。如果是这样的话,时间复杂度是O(n21)。51090942171709440000种状态需要你去遍历,我直接吐了。写完一道题来着,最后发现跑了十多分钟一点反应没有。

首先这个题很明显用记忆化搜索,但是我当时用的String存状态,这样没法存储完整状态。因为你要去dp数组查找的一定是情况一样的,也就是21位数都要满足。这里就需要用到状态压缩了。
状态压缩是使用某种方法,简明扼要地以最小代价来表示某种状态,通常是用一串01数字(二进制数)来表示各个点的状态。这就要求使用状态压缩的对象的点的状态必须只有两种,0 或 1;

https://blog.csdn.net/weixin_45697774/article/details/104874248

我们用1代表走过了这个教学楼,那么是初始状态:10000....0000,如果我们第二次去3号楼,那么状态就变成:10100000.....0000。那么用if去判断走没走过一个教学楼呢?我们设状态为state。如果state>>i&1为1,那么这个第21-i位就是1,也就代表着这个教学楼走过了。

 		for(int i=19;i>=0;i--)   这是最终代码的一部分
		{
			if((state>>i&1)==1||!matrix[21-pre][21-i]) 
				continue;
		}

其次还有就是如何维护dp数组呢?我们用ans记录最终的答案数,在遍历的时候,我们只需要用个t记住在没dfs一个状态的值,最后dfs这个状态,那么ans可能变了,也可能没变。但是ans-t就是dp[state][X]的值。
如果没懂就配合代码食用…

public class new_class {
	static long ans=0;
	static long[][] dp=new long[1<<22][22];
	static boolean[][] matrix=new boolean[22][22];
	public static void main(String[] args) {
		for(int i=1;i<22;i++)
			for(int j=i+1;j<22;j++)
				if(gcd(i,j)==1)
					matrix[i][j]=matrix[j][i]=true;
		for(int i=0;i<1<<22;i++)
			Arrays.fill(dp[i], -1);
		dfs(1<<20,20);   //第二个参数是20代表着,我们这个状态的上一次操作是21-20=1。
		//为什么要这样设置呢,因为dfs里面用起来就方便了。有点绕,理解就好。
		System.out.println(ans);
	}
	
	  static void dfs(int state,int pre) 
	  {
		  if(state==(1<<21)-1)   //如果所有的点都遍历,那么ans++。
		  //不用判断最后一个教学楼是否能到1号教学楼,因为1号教学楼其实和所有的教学楼都是通的。
		  {
			  ans++;
			  return;
		  }
		  Queue<Integer> Q =new LinkedList<Integer>();
		  for(int i=19;i>=0;i--)
		  {
			  if((state>>i&1)==1||!matrix[21-pre][21-i]) 
				  continue;
			  Q.add(i);    
		  }
		  if(Q.size()==0) return;
		  long t;
		  while(Q.size()!=0)
		  {
			  if(dp[state][Q.peek()]!=-1)
			  {
				  ans+=dp[state][Q.peek()];
				  Q.poll();
				  continue;
			  }
			  t=ans;
			  dfs(state+(1<<Q.peek()), Q.peek());
			  dp[state][Q.poll()]=ans-t;   //通过上面的dfs,差值就是这个状态对应的值
		  }
	  }
	  
	static int gcd(int a,int b) 
	{
		if(a%b==0)
			return b;
		return gcd(b, a%b);
	}
}

程序耗时为:1.5s

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值