UVA 1218 完美的服务

https://vjudge.net/problem/UVA-1218

这题注意边界情况。在叶子节点d【u】【2】要注意区别取值,我这里采用的maxn标记。
而且d【u】【1】的取值,在u的孩子中如果有叶子节点那么这个策略是行不通的,也要标记。
接下来就是深度搜索了。我跟网上其他人的做法不太一样。这是一个比较易于理解的做法,写出来发现可以过就没再优化。其实计算d【u】【2】可以再快一点,具体看下面分析。

(u,0):u是服务器,则每个子结点可以是服务器也可以不是。
d(u,1):u不是服务器,但u的父亲是服务器,这意味着u的所有子结点都不是服务器。d(u,2):u和u的父亲都不是服务器。这意味着u恰好有一个儿子是服务器。
状态转移比前面复杂一些,但也不困难。首先可以写出:
d(u,0) = sum{min(d(v,0), d(v,1))} + 1
d(u,1) = sum(d(v,2))
而d(u,2)稍微复杂一点,需要枚举当服务器的子结点编号v,然后把其他所有子结
点v’的d(v’,2)加起来,再和d(v,0)相加。不过如果这样做,每次枚举v都需要O(k)时间(其中k是u的子结点数目),而v本身要枚举k次,因此计算d(u,2)需要花O(k 2 )时间。
刚才的做法有很多重复计算,其实可以利用已经算出的d(u,1)写出一个新的状态转移方程:
d(u,2) = min(d(u,1) – d(v,2) + d(v,0))

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string.h>
#define maxn 10000+10
#define INF 100000000
vector<int> child[maxn];
int n;
int d[maxn][5];
int vis[maxn];
void initial()
{
    for(int i=0;i<=n;i++)
        child[i].clear();
    memset(d,0,sizeof(d));
}

void dfs(int u)
{
    int size=child[u].size();
    if(!size)//leaf node
    {
        d[u][0]=1;
        d[u][1]=0;
        d[u][2]=maxn;//
        return;
    }

    for(int i=0;i<size;i++)
    {
        int v=child[u][i];
        dfs(v);
        d[u][0]+=min(d[v][0],d[v][1]);      
    }
    d[u][0]++;

    for(int i=0;i<size;i++)
    {
        int v=child[u][i];      
        if(!child[v].size())//leaf node
        {
            d[u][1]=maxn;break;
        }
        d[u][1]+=d[v][2];
    }

    int minn=INF;
    for(int i=0;i<size;i++)
    {
        int v=child[u][i];
        int sum=d[v][0];
        for(int j=0;j<size;j++)
        {
            int v1=child[u][j];
            if(j==i)continue;
            sum+=(d[v1][2]>=maxn?0:d[v1][2]);
        }
        minn=min(minn,sum);
    }
    d[u][2]=minn;

    return;
}

int main()
{
    cin>>n;
    while(1)
    {
        initial();
        int n1,n2,root;
        cin>>n1>>n2;
        vis[n1]=vis[n2]=1;

        root=n1;
        child[n1].push_back(n2);
        for(int i=1;i<=n-2;i++)
        {
            cin>>n1>>n2;

            if(vis[n1])
            {child[n1].push_back(n2);vis[n2]=1;}
            else
            {child[n2].push_back(n1);vis[n1]=1;}
        }

        dfs(root);

        cin>>n;
        int a=min(d[root][0],d[root][2]);
        cout<<a<<endl;      

        if(n==-1)
        break;
        cin>>n;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值