题目描述
阿楷很喜欢玩计算机游戏,特别是战略游戏,但是有时他不能尽快找到解所以常常感到很沮丧。现在面临如下问题:他必须在一个中世纪的城堡里设防,城堡里的道路形成一棵无向树。要在结点上安排最少的士兵使得他们可以看到所有边。你能帮助他吗?
你的任务是给出士兵的最少数目。
输入包含多组数据。每组数据表示一棵树,在每组数据中:
第一行是结点的数目。
接下来的几行,每行按如下格式描述一个结点:
结点标识符 : ( 道路的数目 ) 结点标识符1 结点标识符2 …… 结点标识符道路的数目
或者
结点标识符 : (0)
对于 n (0<n<=1500)
个结点,结点标识符是一个从 0 到 n - 1 的整数。每条边在测试用例中只出现一次。
对于每组数据,各给出一个整数表示士兵的最少数目.
测试用例
输入
4
0:(1) 1
1:(2) 2 3
2:(0)
3:(0)
5
3:(3) 1 4 2
1:(1) 0
2:(0)
0:(0)
4:(0)
输出
1
2
解析
这是一道DP或贪心题,我是用贪心写的,飘过,这时间差点跪了。。。老师你不能再卡了。。。还有这内存也是。。。可能是我写法的原因
思路
每次删去叶子结点和其父结点,然后士兵是放在父结点。一轮操作完成后发现问题的规模变小了但性质没有变,并且每次所删结点对你而言都是当前最优解,标准贪心问题。
代码
/*
* 贪心
* 每次删去叶子结点及父结点
* 每一轮操作完成后发现问题规模变小但性质没变
*/
#include<iostream>
#include<cstring>
#define NUM 1503
using namespace std;
struct Node // 用邻接表记录无向树的连接关系
{
int degree = 0;
int to_link[NUM] = { 0 };
int pointer = 0;
};
int main()
{
int count = 0;
while (~scanf("%d", &count))
{
int out_num = 0;
Node *node = new Node[count];
int node_1, link_num, node_2;
for (int i = 0; i < count; i++)
{
scanf("%d:(%d)", &node_1, &link_num);
for (int j = 0; j < link_num; j++)
{
scanf("%d", &node_2);
node[node_1].degree++;
node[node_2].degree++;
node[node_1].to_link[node[node_1].pointer++] = node_2;
node[node_2].to_link[node[node_2].pointer++] = node_1;
}
}
int remain = count; // 剩余人数
while (remain > 0)
{
for (int i = 0; i < count; i++)
{
if (node[i].degree == 1)
{
node[i].degree = 0;
remain--;
int j = 0;
while (node[node[i].to_link[j]].degree == 1
&& j < node[i].pointer)
j++;
int node_f = node[i].to_link[j];
node[node_f].degree = 0;
out_num++; // 士兵放在父结点上
remain--;
for (int k = 0; k < node[node_f].pointer; k++) // 叶子结点的父结点的连接情况
{
if (node[node[node_f].to_link[k]].degree == 1) // 连接的还是叶子结点则直接删去
{
node[node[node_f].to_link[k]].degree = 0;
remain--;
}
else
node[node[node_f].to_link[k]].degree--;
}
}
}
}
delete[] node;
printf("%d\n", out_num);
}
return 0;
}
参考
一样的思路,我主要参考了这个的代码结构,因为之前交的总是有问题,emmmm
不过这代码不能AC,会在第三个用例TLE,这个代码用的是邻接矩阵记录无向树,会在循环遍历时耗费大量时间,我是用邻接表的。还有这代码我是做了修改的,跟原来版本有点差别。
#include<iostream>
#include<cstring>
#define NUM 1503
using namespace std;
int degree[NUM], link_matrix[NUM][NUM]; // 用邻接矩阵记录无向树的连接关系
int main()
{
int count;
while (~scanf("%d", &count))
{
int out_num = 0;
memset(link_matrix, 0, sizeof(link_matrix));
memset(degree, 0, sizeof(degree));
int node_1, link_num, node_2;
for (int i = 0; i < count; i++)
{
scanf("%d:(%d)", &node_1, &link_num);
degree[node_1] = link_num;
for (int j = 0; j < link_num; j++)
{
scanf("%d", &node_2);
link_matrix[node_1][node_2] = 1;
}
}
for (int i = 0; i < count; i++)
{
if (degree[i] == 0)
continue;
for (int j = 0; j < count; j++)
{
if (link_matrix[i][j] == 1 && link_matrix[j][i] == 0)
{
link_matrix[j][i] = 1;
degree[j]++;
}
}
}
int remain = count;
while (remain > 0)
{
for (int i = 0; i < count; i++)
{
if (degree[i] == 1)
{
degree[i]--;
remain--;
int j = 0;
while (link_matrix[i][j] == 0)j++;
link_matrix[i][j] = link_matrix[j][i] = 0;
degree[j] = 0;
out_num++;
remain--;
for (int k = 0; k < count; k++)
{
if (link_matrix[j][k] == 1)
{
if (degree[k] == 1)
{
link_matrix[j][k] = 0;
remain--;
degree[k] = 0;
}
else
degree[k]--;
}
}
}
}
}
printf("%d\n", out_num);
}
return 0;
}