《算法竞赛-训练指南》第一章-1.15_LA 3902

这道题目我真的有点泪奔了!

写了整整一下午才A掉,这是一道比较考察综合知识的题目,要求你对DFS,要求你对模型的建立,要求你对图的掌握要非常熟练。如果是不熟练的话,你是写不出来的。


这个题目的意思是,给你N个结点,代表着客户端和服务端,客户端仅为这个无根树的叶结点时,其余的就是服务端了,但是有一个是发射信号的最终客户端,就要求着,如果有客户端距离服务端太远(距离超过所要求的K)的话,就无法正确的接受到信号了,所以就要重新再建立一个服务端了。题目要求求出需要建立的最少的服务端。


做题的步骤应该是这样的:首先将无根树转化成有根树,无根树转化成有根数的算法就是DFS,将所有的结点搜到,搜索的时候一定要记住要把结点的关系转化成功,也就是,将结点和结点所对应的父亲存好。


这个过程中, DFS能得意进行下去的条件也是至关重要的。if (v != f)也就是不能让他回搜。形成循环,所有的这样双向图都应该是这样判断的。


(中间啊有个小插曲,就是我突然就想看看自己到底打字用几个手指头了,才用了七个,还有三个没有用到啊)

这个还有个很好的知识点,就是将所有待解决的结点,按照深度的不同另外存储起来,然后遍历所有的可能的深度,就可以求出解,当然求可能的时候要求最优解。也就是求距离叶结点最大可满足距离的那个结点。然后循着这个结点DFS所有的叶子结点,标记就行了。


非常的不好意思,为了看自己到底使用几个手指头打字,我发现现在自己都不能好好的打字了,所有有点写的言不达意,很抱歉啊。


贴出代码,我要适应一下正确的打字方法:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <string>

using namespace std;

const int MAXN = 1022;

int N;

int root;

int K;

int a, b;

vector <int> G[MAXN], node[MAXN];

int father[MAXN];

int vis[MAXN];

void converse(int v, int f, int d)
{
	father[v] = f;
	int cnt = G[v].size();
	if (d > K && cnt == 1) //如果这个服务器已经覆盖不到了,而且也是个叶子节点,那么先把它存起来. 
	{
		 node[d].push_back(v);
	}	
	for (int i = 0; i < cnt; i++)
	{
		int t = G[v][i];
		if (t != f) //这里是t!=f????我要去死!!! 
		{
			converse(t, v, d + 1);
		}
	}
}


void DFS(int v, int f, int d)
{
	if (d > K)
	{
		return ;
	}
	int nc = G[v].size();
	if (nc == 1 && d <= K)
	{
		vis[v] = 1;
		return ;
	}
	for (int i = 0; i < nc; i++)
	{
		int t = G[v][i];
		if (t != f)
		{
			DFS(t, v, d + 1);
		}
	}
}


/*

void DFS(int u, int f, int d)
{
	vis[u] = 1;
	int nc = G[u].size();
	for (int i = 0; i < nc; i++)
	{
		int v = G[u][i];
		if (v != f && d < K)
		{
			DFS(v, u, d + 1);
		}
	}
}
*/

int solve()
{
	int cnt = 0;
	memset(vis, 0, sizeof(vis));
	for (int i = N - 1; i > K; i--)
	{
		int nc = node[i].size(); //遍历那些没有覆盖的点,从深处开始. 
		
		for (int j = 0; j < nc; j++)
		{
			int v = node[i][j];
			if (!vis[v])
			{
				for (int k = 0; k < K; k++)
				{
					v = father[v];
				}
				DFS(v, -1, 0);
				cnt++;
			}
		} 
	}
	return cnt;
}

void init()
{
	memset(father, 0, sizeof(father));
	for (int i = 1; i <= N; i++) //我都想死了!!!!这初始化!!!~~~我真!! 
	{
		G[i].clear();
		node[i].clear();
	}
}


int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d", &N);
		scanf("%d%d", &root, &K);
		init();
		for (int i = 0; i < (N - 1); i++) //这里使用的是邻接表建图,因为数据量实在是不小1000; 
		{
			scanf("%d%d", &a, &b);
			G[a].push_back(b);
			G[b].push_back(a);
		}

		converse(root, -1, 0);
		int ans = solve();
		
		printf("%d\n", ans);
	}
//	system("pause");
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值