现在的位置: 首页 > 综合 > 正文

hdu – 1054 – Strategic Game(树形dp)

2019年08月29日 ⁄ 综合 ⁄ 共 1248字 ⁄ 字号 评论关闭

题意:一棵n个结点的无根树(0 < n <= 1500),在一个结点放一个士兵,可以守护与这个点相邻的所有边,问最少需要多少个士兵,可以守护所有边。

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1054

——>>状态:

dp[i][1]表示以 i 为结点的子树,在 i 放一个士兵,可以守护所有边的最少士兵数。

dp[i][0]表示以 i 为结点的子树,在 i 不放士兵,可以守护所有边的最少士兵数。

状态转移方程(结点 j 是结点 i 的儿子):

dp[i][1] += min(dp[j][1], dp[j][0]);(如果结点 i 放了士兵,那么 i 连向其儿子的边已被守护,所以其儿子可放可不放士兵)

dp[i][0] += dp[j][1];(如果结点 i 不放士兵,那么 i 连向其儿子的边必须由其儿子守护)

对于叶子结点i:

dp[i][1] = 1;(虽然以叶子为根的子树无边,但因放了一个士兵在根,所以是1)

dp[i][0] = 0;

#include <cstdio>
#include <cstring>
#include <algorithm>

using std::min;

const int MAXN = 1500 + 10;

struct EDGE
{
    int nTo;
    int nNext;
};

int nHead[MAXN];
int nEdge;
int dp[MAXN][2];
EDGE edge[MAXN << 1];

void Init()
{
    nEdge = 0;
    memset(nHead, -1, sizeof(nHead));
}

void AddEdge(int nFrom, int nTo)
{
    edge[nEdge].nTo = nTo;
    edge[nEdge].nNext = nHead[nFrom];
    nHead[nFrom] = nEdge++;
}

void Read(int n)
{
    int nFrom;
    int nCnt;
    int nTo;

    for (int i = 0; i < n; ++i)
    {
        scanf("%d:(%d)", &nFrom, &nCnt);
        for (int j = 0; j < nCnt; ++j)
        {
            scanf("%d", &nTo);
            AddEdge(nFrom, nTo);
            AddEdge(nTo, nFrom);
        }
    }
}

void Dfs(int i, int nFather)
{
    dp[i][1] = 1;
    dp[i][0] = 0;

    if (nHead[i] == -1) return;

    for (int e = nHead[i]; e != -1; e = edge[e].nNext)
    {
        int j = edge[e].nTo;
        if (j != nFather)
        {
            Dfs(j, i);
            dp[i][1] += min(dp[j][1], dp[j][0]);
            dp[i][0] += dp[j][1];
        }
    }
}

void Output()
{
    printf("%d\n", min(dp[0][1], dp[0][0]));
}

int main()
{
    int n;

    while (scanf("%d", &n) == 1)
    {
        Init();
        Read(n);
        Dfs(0, -1);
        Output();
    }

    return 0;
}

抱歉!评论已关闭.