CF388B Fox and Minimal path 题解

CF388B Fox and Minimal path 题解

题目大意

要求构造一个含有 N ( 1 ⩽ N ⩽ 1000 ) N(1\leqslant N\leqslant 1000) N(1N1000) 个节点的简单无向图,使得从 1 1 1 号节点到 2 2 2 号节点恰有 K K K 条最短路径( 1 ≤ K ≤ 1 0 9 1\le K\le 10^9 1K109 )。输出你构造图的邻接矩阵表示。

解析

step 1:建层连边

这道题要求我们构造一个图,使得从 1 1 1 号节点到 2 2 2 号节点恰有 K K K 条最短路径。我们可以想到二进制拆分来做。我们联想到 Dijkstra ,将 1 1 1 号节点看做是源点。如果将 k k k 拆分成二进制的形式,那我们只需构造出距离源点有 2 0 2^0 20 2 1 2^1 21 2 2 2^2 22,……条最短路就可以了。

接下来的目标,就是想办法构造出距离源点为 2 0 2^0 20 2 1 2^1 21 2 2 2^2 22,…… 的节点。通过观察发现,这是等比数列,公比为 2 2 2 。于是,我们可以分出 s = l o g 2 k + 1 s=log_2 k+1 s=log2k+1 层,每层有 2 2 2 个节点,每层节点之间不相连,相邻两层之间都要连上边,如下图所示:

但是,我们还要将源点放上去,也就是这样:

于是,可以得出,源点到第 i i i 行的节点有 2 i − 1 2^{i-1} 2i1 条最短路。然后,在考虑每个节点的编号问题。首先, 1 1 1 号和 2 2 2 号已经被用掉了,所以从 3 3 3 号开始考虑。从简单方便的方面去想,第 i i i 层的节点分别是 2 i + 1 2i+1 2i+1 2 i + 2 2i+2 2i+2 。接着,再是连边的事情。刚才提到了,第 i i i 层的节点分别是 2 i + 1 2i+1 2i+1 2 i + 2 2i+2 2i+2 ,可以得出第 i i i 层的节点分别是 2 i + 3 2i+3 2i+3 2 i + 4 2i+4 2i+4 ,所以有这几条边: ( 2 i + 1 , 2 i + 3 ) (2i+1,2i+3) (2i+1,2i+3) ( 2 i + 1 , 2 i + 4 ) (2i+1,2i+4) (2i+1,2i+4) ( 2 i + 2 , 2 i + 3 ) (2i+2,2i+3) (2i+2,2i+3) ( 2 i + 2 , 2 i + 4 ) (2i+2,2i+4) (2i+2,2i+4)

2.建链拉边

建好了几个点之后,就想办法把这些点连在一起。我们在旁边再拉一条链出来,那就是这样:

为了方便研究,我们在点上标源点到这个点的路径数

接下来就好办了。假设 k = 5 = 1 + 4 = 2 0 + 2 2 k=5=1+4=2^0+2^2 k=5=1+4=20+22,那就把第 1 1 1 层和第 3 3 3 层的其中一个节点与链上对应的点连边即可,就像这样子:

就达到了目的。接下来,还是考虑节点编号的问题。首先,之前已经用掉了 2 s + 2 2s+2 2s+2 个点,那么这一条链就从 2 s + 3 2s+3 2s+3 开始。注意,最后一个点的编号应该是 2 2 2 ,这里需要特判一下。

那么思路上大体就是这样了。

算法流程

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int s = 0, w = 1;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; w *= (ch == '-') ? -1 : 1, ch = getchar());
	for(; ch >= '0' && ch <= '9'; s = 10 * s + ch - '0', ch = getchar());
	return s * w;
}
const int MAXN = 1005;
bool G[MAXN][MAXN];
void add(int u, int v){
	G[u][v] = G[v][u] = 1;
}
signed main(){
	int k = read(), s = log2(k);
	add(1, 3), add(1, 4);
	for(int i = 1; i <= s; i++){
		add(i * 2 + 1, i * 2 + 3);
		add(i * 2 + 1, i * 2 + 4);
		add(i * 2 + 2, i * 2 + 3);
		add(i * 2 + 2, i * 2 + 4);
	}
	for(int i = 1; i <= s; i++){
		add(2 * s + 4 + i, 2 * s + i + 5);
	}
	add(3 * s + 4, 2);
	for(int i = 0; i <= s; i++){
		if((k & (1 << i)) == 0) continue;
		if(i == s) add(2 * i + 4, 2);
		else add(2 * i + 4, 2 * s + i + 5);
	}
	cout << 3 * s + 4 << endl;
	for(int i = 1; i <= 3 * s + 4; i++){
		for(int j = 1; j <= 3 * s + 4; j++){
			cout << (G[i][j] ? "Y" : "N");
		}
		puts("");
	}
	return 0;
}

制作不易,顺手点个赞吧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值