Perfect Service UVA

来源:互联网 发布:php特殊字符转义函数 编辑:程序博客网 时间:2024/06/08 05:27
树形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;}




原创粉丝点击