2. 小游戏

题目描述

阿楷很喜欢玩计算机游戏,特别是战略游戏,但是有时他不能尽快找到解所以常常感到很沮丧。现在面临如下问题:他必须在一个中世纪的城堡里设防,城堡里的道路形成一棵无向树。要在结点上安排最少的士兵使得他们可以看到所有边。你能帮助他吗?

你的任务是给出士兵的最少数目。
输入包含多组数据。每组数据表示一棵树,在每组数据中:
第一行是结点的数目。
接下来的几行,每行按如下格式描述一个结点:
结点标识符 : ( 道路的数目 ) 结点标识符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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值