POJ 3398 Perfect Service(树形DP)

58 篇文章 1 订阅

Description
在一个n个点n-1条边的计算机网络中选取一些计算机做服务器,对于服务器的设置,要求每台电脑要么是服务器要么是与服务器相连,但是一台电脑如果不是服务器那么其不能和多台服务器相连,但是服务器可以与服务器相连,问最少要设置多少台服务器
Input
多组用例,每组用例第一行为一整数n表示点数,之后n-1行每行两个整数a和b表示a和b之间有一条边,每组用例以0结束,以-1结束全部输入
Output
对于每组用例,输出最少需要设置的服务器数量
Sample Input
6
1 3
2 3
3 4
4 5
4 6
0
2
1 2
-1
Sample Output
2
1
Solution
dp[i][0]表示i是服务器并且以i为根的子树都被覆盖的情况下服务器的最少点数
dp[i][1]表示i不属于服务器,且以i为根的子树都被覆盖,且i被其中不少于一个子节点覆盖的情况下服务器的最少点数
dp[i][2]表示i不属于服务器,且以i为根的子树都被覆盖,且i没被子节点覆盖的情况下服务器的最少点数
dp[i][0]=1+sum(min(dp[u][0],dp[u][2]))
dp[i][1]=INF i没有子节点
dp[i][1]=sum(min(dp[u][0],dp[u][1]))+inc i有子节点
inc=0若sum(min(dp[u][0],dp[u][1]))包含某个dp[u][0]
否则inc=min(dp[u][0]-dp[u][1])
dp[i][2]=sum(dp[u][1])
结果即为min(dp[1][0],dp[1][1])
Code

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define maxn 11111
#define INF 0x3f3f3f3f
typedef long long ll;
struct Edge
{
    int to,next;
}edge[2*maxn];
int n,head[maxn],tot;
int dp[maxn][3];
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
    for(int i=0;i<maxn;i++)dp[i][1]=INF;
}
void add(int u,int v)
{
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void DP(int u,int fa)
{
    dp[u][0]=1,dp[u][2]=0;
    int sum=0,inc=INF,flag=0;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa)continue;
        DP(v,u);
        dp[u][0]+=min(dp[v][0],dp[v][2]);
        if(dp[v][0]<=dp[v][1])
            sum+=dp[v][0],flag=1;
        else sum+=dp[v][1],inc=min(inc,dp[v][0]-dp[v][1]);
        if(dp[v][1]!=INF&&dp[u][2]!=INF)dp[u][2]+=dp[v][1];
        else dp[u][2]=INF;
    }
    if(inc!=INF&&!flag)dp[u][1]=INF;
    else
    {
        dp[u][1]=sum;
        if(!flag)dp[u][1]+=inc;
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        init();
        int u,v,t;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v),add(v,u);
        }
        DP(1,1);
        int ans=min(dp[1][0],dp[1][1]);
        printf("%d\n",ans);
        scanf("%d",&t);
        if(t!=0)break;
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值