[2016ACM多校] HDU5758 贪心 搜索

题意

一棵树,要遍历所有的边,不能往回走。走到尽头的时候可以传送到另一个点,首先要保证使用传送次数最少。

思路

所走的路径是最小链覆盖,由两个两个叶子的链组成,如果有奇数个子叶节点,那么会多一条从某一个叶子到祖先的一条特殊路径。
那么对于每一个叶节点,一定有且只有一条指向祖先的路径。对于一个非叶,会收到来自子节点上传的多条路径,那么这时两两合并一定最优,但是又必须要保证它向根的路径被覆盖。所以如果上传了奇数个路径那么正好把多余的一个继续上传,如果是偶数个路径则要一次上传两个。如果共有偶数个叶节点,那么在根上就会两两合并。如果是奇数个叶节点,就要考虑用多出的那条路径来省去一些偶数上传。
可以发现,对于上述过程在贪心回溯后,其实那个多出的路径已经连接到了根,那么我们就搜索一下可以用它来优化那些偶数路径。基本的想法是搜索从根出发的最长偶数(只可能是2)路径然后答案减去这个长度。但是最优解可能那个多出的路径根本每到根或者越过某些1流量的边继续优化下面的长偶路径了。所以在搜索过程中遇到偶数路径长度+1,遇到奇数路径长度-1,全局记录最大长度,答案减去这个最大长度即可。

下面论证这一做法对于多出路径为到根的正确性。如果不让那条奇数路径到根,那么我们需要从另一方向引两条路径来填补这条奇数路径来的方向,这正好是符合-1(即答案+1),而到了拐点,接下来搜索的最大“长度”正好表示了用这条路径改接所能达到的效果。请看图。

引入多出的奇数边优化
注意!搜索起点的根最好是非叶节点。

AC代码 C++

#include <stdio.h>
#include <string.h>
#include <vector>

using namespace std;

#define MAXN 100005

vector<int> G[MAXN];
bool eve[MAXN];
int ans;
int dmax;

int dfs(int u, int fa)
{
    int i, v, cnt=0;
    bool isleaf = true;
    for(i=G[u].size(); i--;)
    {
        v = G[u][i];
        if(v != fa)
        {
            cnt += dfs(v, u);
            isleaf = false;
            if(eve[v])
                ans++;
        }
    }
    if(isleaf || cnt & 1)
        return 1;
    eve[u] = true;
    return 2;
}

void fd(int u, int fa, int d)
{
    if(d > dmax)
        dmax = d;
    int i, v;
    for(i=G[u].size(); i--;)
    {
        v = G[u][i];
        if(v != fa)
            fd(v, u, d + (eve[v] ? 1 : -1));
    }
}

int main()
{
    int t, n, i, x, y, cnt;
    scanf("%d", &t);
    while(t-- && scanf("%d", &n) > 0)
    {
        memset(eve, false, sizeof eve);
        for(i=1; i<=n; i++)
            G[i].clear();
        for(i=1; i<n; i++)
        {
            scanf("%d%d", &x, &y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        ans = n - 1;
        for(x=1; G[x].size()==1; x++);
        cnt = dfs(x, 0);
        if((cnt & 1) == 0)
        {
            printf("%d\n", ans);
            continue;
        }
        dmax = 0;
        fd(x, 0, 0);
        printf("%d\n", ans - dmax);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值