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

题目

蓝桥学院由 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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值