bzoj 4464: [Jsoi2013]旅行时的困惑 dp+贪心

针对Waldives政府希望在已有快艇专线基础上,通过新建最少公交线路实现任意岛屿间可达的问题,本文提出一种基于树型图的最小路径覆盖算法。通过递归深度优先搜索,结合动态规划思想,算法有效地计算出所需的最少公交线路数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

Waldives 有 N 个小岛。目前的交通系统中包含 N-1 条快艇专线,每条快艇
专线连接两个岛。这 N-1条快艇专线恰好形成了一棵树。
由于特殊的原因,所有N-1条快艇专线都是单向的。这导致了很多岛屿之间
不能相互到达。因此,Waldives 政府希望新建一些公交线路,使得建设完毕后,
任意两个小岛都可以互相到达。为了节约开支,政府希望建设最少的公交线路。
同时,出于规划考虑,每一条公交线路都有如下的要求:
1、每一条交通线路包含若干条连续的快艇专线,你可以认为一条公交线路
对应树上的一条路径,而其所包含的若干快艇专线则对应树上被这条路
径所覆盖的树边(也就是之前已经存在的某个快艇专线);
2、显然一条交通线路只能覆盖树上任意一条边至多一次;
3、公交线路中所包含的每一个快艇专线都是有方向的,并且与其所覆盖的
树边的方向相反;
4、不同的公交线路可以覆盖树上相同的点或者相同的边。
Waldives 的 N 个岛屿分别从 0 到 N-1 编号。现在给出 Waldives 已有的快艇
专线信息,请计算最少所需要新建的交通线路的数量。

Input

第一行包含一个整数 N。 接下来 N-1行,每行包含两个整数 x,y。表示存在一条从岛屿 x开往岛屿y的快艇专线。 N < = 100000

Output

输出一行一个整数,表示需要建设的最少的交通线路数量。

Sample Input

4

0 1

1 2

1 3
Sample Output

2
HINT

样例如下图所示。图中给出了一个可行的最佳方案。

其中黑色的实边代表原先已经存在的快艇专线, 而虚边则对新加入的公交线

路,分别为 2->1->0和3->1->0。

注意直接新建公交线路 3->2 是不允许的,这并不是一条树上的合法路径,

因为编号为3的点与编号为1的点在树中并不直接相连。同样的,建立公交路线

2->1->3也是不允许的,因为这条路线中包含快艇专线1->3,这并没有和已有的

专线的方向相反

在这里插入图片描述

分析:
题目就是要求对一个树型图进行最小路径覆盖(可以使用已经被覆盖的边)。
这题非常像今年noipD1T3。
我们设f[x]f[x]f[x],如果当前边是父亲走向儿子的边,表示最优覆盖了这棵子树时,最多留下多少条以儿子为起点的路径;如果当前边是儿子走向父亲的,表示最优覆盖了这棵子树时,最多留下多少条以儿子为终点的路径。
儿子走向当前节点时,会提供d=max(1,f[son])d=max(1,f[son])d=max(1,f[son])条这样的路径,因为这条边也要被覆盖。
我们设num1num_1num1表示儿子走向父亲的ddd值和,num2num_2num2表示父亲走向儿子的ddd值和。
直接当前点两两排列,考虑哪一种会多出来。假设num1num_1num1多出来,而当前节点到它父亲的边是父亲指向儿子的,那么num1num_1num1没有用,直接统计进答案;否则就传到它的父亲。

代码:

/**************************************************************
    Problem: 4464
    User: liangzihao
    Language: C++
    Result: Accepted
    Time:224 ms
    Memory:5704 kb
****************************************************************/
 
#include <iostream>
#include <cstdio>
#include <cmath>
 
const int maxn=1e5+7;
 
using namespace std;
 
int n,x,y,cnt,ans;
int f[maxn],ls[maxn];
 
struct edge{
    int y,op,next;
}g[maxn*2];
 
void add(int x,int y)
{
    g[++cnt]=(edge){y,1,ls[x]};
    ls[x]=cnt;
    g[++cnt]=(edge){x,0,ls[y]};
    ls[y]=cnt;
}
 
void dfs(int x,int fa,int op)
{
    int num1=0,num2=0;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (y==fa) continue;
        dfs(y,x,g[i].op);
        if (!g[i].op) num1+=max(f[y],1); //第一类路径和第二类路径进行统计
                 else num2+=max(f[y],1);
    }
    if (num1>num2) //第一类路径多了
    {
        ans+=num2; //第二类路径都可以统计进答案
        if (op) ans+=num1-num2; //当前多余的路径无法上传,只能统计进答案
           else f[x]=num1-num2;  //可以上传则保留
    }
    else
    {
        ans+=num1; //第一类路径统计进答案
        if (op) f[x]=num2-num1;  //可以上传
           else ans+=num2-num1; //不可上传
    }
}
 
int main()
{
    scanf("%d",&n);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        x++,y++;
        add(x,y);
    }   
    dfs(1,0,0);
    ans+=f[1];
    printf("%d\n",ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值