一问一答的在线编程测试题小记

来源:互联网 发布:淘宝为什么不能付款 编辑:程序博客网 时间:2024/05/16 01:38

闲话:

今天做一问一答的在线编程题目玩,小记如下。

算是给我以后写的文档也整理一下思路,最好也能够帮到正在学习编程的朋友们。

链接:http://wenda60.com/programs/index.html

前四个题没有什么难度,很快就做出来了。但是我的“纯在线编程”(不用本地编译器,不调试,直接编码运行),还是有点挑战的,各种小错误,也是因为我嫌机器慢没有开IDE。

第5个题顶不住了,开IDE调试。

题目3.1 小兔齐齐跑无向图:

详细问题链接:http://wenda60.com/programs/view/id-548.html

给定一个无向图,小兔齐齐一开始位于某个节点上(我们不知道他具体在哪个节点上)。我们知道每一秒小兔都必定会向他所在的节点的某一个邻居节点出发(如果存在邻居节点;假设小兔的移动速度很快,移动时间可以忽略)。在每一秒你可以做不超过2次的询问,每次可以询问某个节点是否有小兔!

现在假设小兔不想被我们找到,同时小兔非常聪明,那么我们需要至少几秒才能必定找到小兔的位置?

Input:输入数据第一行包含一个整数T,表示测试数据的组数。对于每组测试数据: 第一行输入两个整数N和M , N图中的节点,M表示边数,1 <= N <= 15 ,1 <= M <= 100 。 接下来输入M行,每行2个正整数 x y, 表示x和y直接有一条边(不存在自环)。 1<=x,y<=N Output:对于每组数据输出一行,如果你最终可以找到小兔,输出最坏情况下需要几秒才能知道小兔的目前位置;否则输出一个 "-1" (不包括引号) 。

Sample Input

2

3 3

1 2

2 3

3 1

4 3

1 2

2 3

3 1

Sample Output

1

2

Hint

第一组样例中: 第1秒查询节点1和2,如果小兔在1或2,则解决;否则小兔必然在3;

第二组样例中: 第1秒查询节点1和2,如果小兔在1或2,则解决;否则小兔可能在3或者4。第2秒查询节点1和2,如果在1或者2,则解决;否则小兔必然在4(如果前1秒是在节点3,那么这一秒肯定不在1就在2)。


分析:

应该没有人傻到每次只选择一个点来判断吧,同样也不能枚举每次取一个点和取两个点的两种情况,第一段的最少意味着我们肯定是选一次2个的这种更高效的方式。
后面我会说明在小兔子每次都会移动的情况下,也只有这种方式才是可行的。
很显然:首先我们需要判断连通分支的数量,因为小兔子只能在一个连通分支内移动,而我们判断它位置的时候,选择测试哪两个点最有效的方式肯定是方在同一个连通分支(先不考虑只有3个不连通的点的这种边界情况)。因为这样能够更快的判断小兔子在不在一个连通分支,这样一个一个的判断。

下面说明为什么在小兔子每回合都移动的情况下只有每次取2个点才行:

由于小兔子在不断的移动中,如果每次只取一个点,小兔子可能和我们擦肩而过。例如,在我示意的这个图中,如果小兔子在F,我们探测了G点,下一步探测F点,而小兔子同时又跑到了G点,依此类推,我们永远无法判断小兔子是否在一个连同分支内。在一次选取2个节点时,同样不能好高骛远,两次之间要有重叠,例如这次是FG,下次应该是FE,再下次应该是EC(这个图还有别的用途,此处先假设CD之间没有边),依次类推下去。可以知道对于一个有n的节点的连通分支,如果它可探测,则需要n-1步来确定小兔子当前在哪。

所以代码首先计算连同分支的数量。

那么什么情况下它不可探测呢?经过构造,我发现如果图中出现了超过3个节点的环,则问题就不可解了。而对于线性的连通分支和有之多3个节点构成的环的连通分支,问题是可解的。线性的好理解,刚刚也说过了,对于有三角形环的情况,可以参见样例的第二组的hint。回到不可解的情况,对于有4个节点的环,如果你坚持检查同样的节点,那么兔子可以在远处的2个节点上来回跳;更一般的说,无论你怎么选这2个节点,如果你没有直接选中兔子(题目要求最大次数,那么我们的RP自然是最低的)那么至少存在一个与兔子相邻的节点在你的探测节点集之外,兔子可以跳往这个节点。

更一般的说,对于一次坚持k个节点的情况,如果存在k+2个节点的环,那么问题就无解了,其他都有解。

一旦发现了:在一次至多坚持2个节点的前提先只要存在4个以及以上节点数目的环,问题就无解了,那么剩下怎么做就好办了。

我们可以求出图中最大的环的大小,如果它大于等于4,那么问题无解。
我一开始用的是一个找最大基本环(不嵌套)的算法,在最大环大小为3的时候,还要判断这些三角形是否有重叠的边,可以合并出大的环。比较麻烦,后来明白过来直接用了找最大环的最简单DFS算法,一切OK。

有解的情况:
各个连通分支相互独立,互不影响,可以认为是一个更小规模的问题。所以可以将各个分支上的解数目加起来得到最终答案。
需要注意的是,对于有多个连通分支的情况,我们要确定兔子在不在一个含有ni个节点的连通分支上,需要ni-1步,而假设已知兔子在某个连通分支上,则需要ni-2步就可以定位兔子。
由于求的是最大值,根据RP最低原则,兔子必定在我们检查的最后一个分支上,因此需要k-1个ni-1和一个ni-2(假设有k个连通分支)。

代码:

set_cpnt函数用于划分连通分支。
largest_ring函数用于查找最大的环,同时将大小为3的所有环保存起来,以备后续使用。
cal_steps函数用以在有解的情况下计算最终结果。

#include <iostream>#include <fstream>#include <string>#include <vector>#include <cstring>#include <algorithm>using namespace std;int T,m,n;bool map[16][16];int component[16];//index is node #int component_size[16];//index is component #int set_cpnt(const int p,const int cn){int res=1;for(int i=1;i<=n;i++){if(map[p][i] && component[i]!=cn){component[i]=cn;res+=set_cpnt(i,cn);}}return res;}int max_ring_size=0;int vis[16];void largest_ring(const int from, const int p,const int d){vis[p]=d;for(int i=1;i<=n;i++){if(!map[p][i])continue;if(vis[i]==0)largest_ring(p,i,d+1);else{int t=vis[p]-vis[i]+1;max_ring_size=max(max_ring_size,t);}}}int cal_steps(const int cn){int res=0;for(int i=1;i<=cn;i++){if(component_size[i]<=2)res++;else//it takes k-1 steps to find out whether the rabbit is in a reslovable component with k nodesres+=component_size[i]-1;}return max(1,res-1);//"res-1" for the global last step, (we can induce it without doing it)//"max(1,..)" for only one component with less than 2 nodes situation.}int main(){char sysInputFile[] = "{sysFileUrl}";ifstream cin(sysInputFile);cin>>T;while(T--){cin>>n>>m;max_ring_size=0;memset(vis,0,sizeof(vis));memset(map,false,sizeof(map));for(int i=1;i<=n;i++){component[i]=component_size[i]=0;}while(m--){int a,b;cin>>a>>b;map[a][b]=map[b][a]=true;}int cn=1;//component numberfor(int i=1;i<=n;i++){if(component[i]==0){component[i]=cn;component_size[i]=set_cpnt(i,cn++);largest_ring(0,i,1);}}cn--;//there is cn componentsint res=0;if(max_ring_size>3)cout<<-1<<endl;elsecout<<cal_steps(cn)<<endl;}return 0;}



原创粉丝点击