Perfect Service UVA - 1218

树形DP
题目描述:求最少的服务器,使得其他不是服务器的计算机恰好和一台服务器计算机相邻。
解题分析:定义dp[u][0] u是服务器,每个子节点可以使服务器也可以不是。
dp[u][0] = sum(min(dp[v][0],dp[v][1])).
dp[u][1] u不是服务器,但u的父亲是服务器,所以u的所有子节点都不是服务器。
dp[u][1] = sum(dp[v][2]).
dp[u][2],u和u的父亲都不是服务器,所以u的儿子中必须恰好有一个儿子是服务器。
dp[u][2] = min(dp[v][0] + sum(others dp[v][2])).如果遍历每一个结点作为选定的服务器儿子,那么时间复杂度将会是k^2,k是子节点的个数。但实际上可以不用这样重复计算,
因为sum(dp[v][2])已经在dp[u][1]里面给计算过了,所以可以利用上面的结果。则
dp[u][2] = min(dp[u][1] - dp[v][1] + dp[v][0]).
第一遍写的代码:
有两点不太明白,一直WA,第一个是在边界处理上,如果u没有子节点的话,dp[u][2] = maxn, 而我之前写的是dp[u][2] = 0;
第二点不明白的是:为什么dp[u][2]的初始化为0x3f3f3f3f就会WA呢,感觉没什么问题啊,哦,明白了,如果初始化为0x3f3f3f3f的话,n个0x3f3f3f3f相加就会数据溢出了。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;

const int maxn = 10000 + 10;
const int INF = 0x3f3f3f3f;
int par[maxn];
vector<int> temp[maxn];
vector<int> son[maxn];
int dp[maxn][3];
int n;

int dfs(int u)
{
     if(son[u].size() == 0)
     {
         dp[u][0] = 1;
         dp[u][1] = 0;
         dp[u][2] = maxn;
     }
     else
     {
         for(int i = 0; i < son[u].size(); i++)
         {
            dfs(son[u][i]);
         }
         int sum0 = 0,sum1 = 0;
         for(int i = 0; i < son[u].size(); i++)
         {
            int v = son[u][i];
            sum0  += min(dp[v][1],dp[v][0]);
            sum1  += dp[v][2];
         }
         dp[u][0] = sum0 + 1;
         dp[u][1] = sum1;
         for(int i = 0; i < son[u].size(); i++)
         {
             int v = son[u][i];
             dp[u][2] = min(dp[u][2],dp[u][1]-dp[v][2] + dp[v][0]);
         }
     }
}

void build_tree(int u,int p)
{
    for(int i = 0;i < temp[u].size(); i++)
    {
        int v = temp[u][i];
        if(v != p)
        {
            par[v] = u;
            build_tree(v,u);
        }
    }
}

int main()
{
    while(cin >> n && n!= -1)
    {
        if(n == 0) continue;
        for(int i = 0; i <= n; i++)
        {
            son[i].clear();
            temp[i].clear();
            par[i] = i;
            dp[i][2] = maxn;
        }
        int x,y;
        for(int i = 0; i < n-1; i++)
        {
            cin >> x >> y;
            temp[y].push_back(x);
            temp[x].push_back(y);
        }
        int root = 1;
        par[root] = 0;
        build_tree(root,0);
        for(int i = 1; i <= n; i++)
        {
            son[par[i]].push_back(i);
        }
        dfs(1);
        printf("%d\n",min(dp[root][0],dp[root][2]));
    }
    return 0;
}
后来发觉有大佬直接对无根树处理,只需要递归的时候判断一下是不是父节点就行
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 10000 + 10;
vector <int> graph[maxn];
int dp[maxn][3];
int n;

void dfs(int u,int fa)
{
    dp[u][0] = 1;
    dp[u][1] = 0;
    dp[u][2] = maxn;
    for(int i = 0; i < graph[u].size(); i++)
    {
        int v = graph[u][i];
        if(v != fa)
        {
            dfs(v,u);
        }
    }
    for(int i = 0; i < graph[u].size(); i++)
    {
        int v = graph[u][i];
        if(v == fa) continue;
        dp[u][0] += min(dp[v][0],dp[v][1]);
        dp[u][1] += dp[v][2];
    }
    for(int i = 0; i < graph[u].size(); i++)
    {
        int v = graph[u][i];
        if(v == fa) continue;
        dp[u][2] = min(dp[u][2],dp[u][1] - dp[v][2] + dp[v][0]);
    }
}

int main()
{
    while(cin >> n && n != -1)
    {
        if(n == 0) continue;
        for(int i = 0; i <= n; i++)
        {
            dp[i][2] = maxn;
            graph[i].clear();
        }
        for(int i = 0; i < n-1; i++)
        {
            int x,y;
            scanf("%d %d",&x,&y);
            graph[x].push_back(y);
            graph[y].push_back(x);
        }
        dfs(1,0);
        printf("%d\n",min(dp[1][0],dp[1][2]));
    }
    return 0;
}




全部评论

相关推荐

点赞 评论 收藏
分享
斑驳不同:还为啥暴躁 假的不骂你骂谁啊
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务