POJ 1463 Strategic game(树形DP)

题意:给一棵树,当在某个结点上放置士兵时,与该点相邻的边就被看守住了,求最少需多少士兵才能看住树中所有边。

分析:先将无根树有根化,然后DP。

状态设计:

      1、dp[i][0],表示在结点 i 没放置士兵的情况下,看住以结点 i 为根的子树的所有边所需的最少士兵;

      2、dp[i][1],表示在结点 i 放置士兵的情况下,看住以结点 i 为根的子树的所有边所需的最少士兵。

状态转移:

      1、dp[i][0]=∑dp[j][1],j 是 i 的儿子结点;(根结点不放士兵时,与其相连的边必须由儿子结点来看守)

      2、dp[i][1]=1+∑ ( MIN ( dp[j][0] , dp[j][1] ) ),j 是 i 的儿子结点。 (根结点放士兵时,儿子结点可放可不放)

初始化:  d[i][0]=0,d[i][1]=1,其中 i 是叶子结点

View Code
#include <stdio.h>
#include <string.h>
#include <vector>
using namespace std;

#define N 1510
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))

int n,p[N],d[N],maxd;
vector<int>g[N];
vector<int>rd[N];
int dp[N][2];

void init()
{
    int i;
    maxd=0;
    for(i=0; i<n; i++)
    {
        g[i].clear();
        rd[i].clear();
    }
    memset(dp,0,sizeof(dp));
}
void read()
{
    int i,j,k,cnt;
    for(k=0; k<n; k++)
    {
        scanf("%d:(%d)",&i,&cnt);
        while(cnt--)
        {
            scanf("%d",&j);
            g[i].push_back(j);
            g[j].push_back(i);
        }
    }
}
void dfs(int u,int fa)
{
    int i,v;
    d[u]=(fa==-1)?0:d[fa]+1;
    rd[d[u]].push_back(u);
    maxd=MAX(maxd,d[u]);
    for(i=0; i<g[u].size(); i++)
    {
        v=g[u][i];
        if(v^fa)    dfs(v,p[v]=u);
    }
}
void DP()
{
    int i,j,u;
    for(i=maxd; i>=0; i--)
    {
        for(j=0; j<rd[i].size(); j++)
        {
            u=rd[i][j];
            dp[u][1]+=1;
            if(i)
            {
                dp[p[u]][0]+=dp[u][1];
                dp[p[u]][1]+=MIN(dp[u][0],dp[u][1]);
            }
        }
    }
    printf("%d\n",MIN(dp[0][0],dp[0][1]));
}
int main()
{
    int i,j,k;
    while(~scanf("%d",&n))
    {
        init();
        read();
        dfs(0,-1);
        DP();
    }
    return 0;
}
原文地址:https://www.cnblogs.com/algorithms/p/2629183.html