POJ 3659 Cell Phone Network【最小支配集、树型dp】

本文详细介绍了如何使用树型dp解决最小支配集问题,以帮助Farmer John部署电话网络,确保每头牛都能互相通话。通过贪心策略和状态转移方程,求解最少需要安装的电话塔数量。此问题适用于理解最小支配集概念及在实际场景中的应用。

Cell Phone Network

Time Limit: 1000MS

 

Memory Limit: 65536K

Total Submissions: 6254

 

Accepted: 2240

Description

Farmer John has decided to give each of his cows a cell phone in hopes to encourage their social interaction. This, however, requires him to set up cell phone towers on his N (1 ≤ N ≤ 10,000) pastures (conveniently numbered 1..N) so they can all communicate.

Exactly N-1 pairs of pastures are adjacent, and for any two pastures A and B (1 ≤ A ≤ N; 1 ≤ B ≤ NA ≠ B) there is a sequence of adjacent pastures such that is the first pasture in the sequence and B is the last. Farmer John can only place cell phone towers in the pastures, and each tower has enough range to provide service to the pasture it is on and all pastures adjacent to the pasture with the cell tower.

Help him determine the minimum number of towers he must install to provide cell phone service to each pasture.

Input

* Line 1: A single integer: N
* Lines 2..N: Each line specifies a pair of adjacent pastures with two space-separated integers: A and B

Output

* Line 1: A single integer indicating the minimum number of towers to install

Sample Input

5

1 3

5 2

4 3

3 5

Sample Output

2

Source

USACO 2008 January Gold

 


题目大意:有n个点,农场主准备给每个点的牛一个电话,如果在某点设立电线杆,那么其相连的点和当前点都可以互相通电话,请问最少设立多少个电线杆才能使得所有点的牛都可以互相打电话。


思路:这是个经典的最小支配集的问题,可以用贪心来解决,也可以用树型dp来搞定,我们这里来详解一下树型dp如何实现。

1、首先既然是dp问题,首先我们要设计dp的数组和其含义:

dp【u】【0】表示点u属于支配集,并且以u为根节点的子树已经全部被覆盖。

dp【u】【1】表示点u不属于支配集,并且以u为根节点的子树已经全部覆盖,并且点u被至少其一个子节点所支配。

dp【u】【2】表示点u不属于支配集,并且以u为根节点的子树已经全部覆盖,并且点u没有被其子节点所支配。(那么不难理解,这个点要被其父节点所支配)


2、对数组进行设计完毕之后,我们就要来考虑状态转移过程,分别讨论三种dp数组是如何转移过来:

无论哪种状态,其想要达到的目的是:以u为根节点,并且包含节点u在内的子树都被支配覆盖


dp【u】【0】:dp【u】【0】表示点u属于支配集,所以其子节点的状态无所厚非,包含u在内的以u为根节点的全部都能被支配覆盖,所以其状态来源和其子节点状态无所相关,我们只要在其子节点中找最优解即可,辣么其状态转移方程:

dp【u】【0】+=1+枚举所有子节点min(dp【v】【0】,dp【v】【1】,dp【v】【2】)


dp【u】【1】:dp【u】【1】表示点u不属于支配集,但是点u被支配着,所以其支配来源一定是从子节点来的,只要其子节点中只要有一个节点属于支配集,那么当前节点u就会被支配,也就是说以u为根节点的子树包含u在内,所有节点都背覆盖了,达到目的。那么不难理解,dp【u】【1】的来源可能是dp【v】【0】或者是dp【v】【1】,并且满足至少一个来源是dp【v】【0】即可。

辣么其状态转移方程:

当然,如果节点u没有子节点:

dp【u】【1】=INF;

如果节点u有子节点:

dp【u】【1】+=枚举所有子节点min(dp【v】【0】,dp【v】【1】)//并且保证其状态转移过程中至少有一个来源于dp【v】【0】,具体参考代码实现。


dp【u】【2】:dp【u】【2】表示点u不属于支配集,并且点u不被其任一子节点所支配,那么其来源就只能是dp【v】【1】或者是dp【v】【2】,但是如果来源是dp【v】【2】的话,那么我们就不能保证两个节点被同时支配了,所以dp【u】【2】只能来源于dp【v】【1】。

d辣么其状态转移方程:
dp【u】【2】+=枚举所有子节点dp【v】【1】;其中任一dp【v】【1】==INF,那么dp【u】【2】=INF。


辣么不难理解,ans=min(dp【root】【0】,dp【root】【1】,1+dp【root】【2】)


3、根节点的确定:我们不妨把图建成双向图,然后任取一点作为根节点即可,从根节点开始向下进行dp操作。


思路构建完毕,具体细节实现,参考代码来实现。


AC代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define INF 0x1f1f1f1f
int head[100000];
struct EdgeNode
{
    int to;
    int w;
    int next;
}e[100000];
int cont;
int dp[5000000][3];
int vis[500000];
void add(int from,int to)
{
    e[cont].to=to;
    e[cont].next=head[from];
    head[from]=cont++;
    e[cont].to=from;
    e[cont].next=head[to];
    head[to]=cont++;
}
void Dp(int u)
{
    dp[u][2]=0;
    dp[u][1]=0;
    dp[u][0]=1;
    int sum=0,inc=INF;
    vis[u]=1;
    bool judge=false;
    for(int k=head[u];k!=-1;k=e[k].next)
    {
        int to=e[k].to;
        if(vis[to]==1)continue;
        Dp(to);
        dp[u][0]+=min(dp[to][0],min(dp[to][1],dp[to][2]));
        if(dp[to][0]<=dp[to][1])
        {
            sum+=dp[to][0];
            judge=true;
        }
        else
        {
            sum+=dp[to][1];
            inc=min(inc,dp[to][0]-dp[to][1]);
        }
        if(dp[to][1]!=INF&&dp[u][2]!=INF)dp[u][2]+=dp[to][1];
        else dp[u][2]=INF;
    }
    if(inc==INF&&judge==false)dp[u][1]=INF;
    else
    {
        dp[u][1]=sum;
        if(judge==false)dp[u][1]+=inc;
    }
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        memset(dp,0,sizeof(dp));
        cont=0;
        for(int i=0;i<n-1;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        Dp(1);
        printf("%d\n",min(min(dp[1][0],dp[1][1]),dp[1][2]+1));
    }
}


【最优潮流】直流最优潮流(OPF)课设(Matlab代码实现)内容概要:本文档主要围绕“直流最优潮流(OPF)课设”的Matlab代码实现展开,属于电力系统优化领域的教学与科研实践内容。文档介绍了通过Matlab进行电力系统最优潮流计算的基本原理与编程实现方法,重点聚焦于直流最优潮流模型的构建与求解过程,适用于课程设计或科研入门实践。文中提及使用YALMIP等优化工具包进行建模,并提供了相关资源下载链接,便于读者复现与学习。此外,文档还列举了大量与电力系统、智能优化算法、机器学习、路径规划等相关的Matlab仿真案例,体现出其服务于科研仿真辅导的综合性平台性质。; 适合人群:电气工程、自动化、电力系统及相关专业的本科生、研究生,以及从事电力系统优化、智能算法应用研究的科研人员。; 使用场景及目标:①掌握直流最优潮流的基本原理与Matlab实现方法;②完成课程设计或科研项目中的电力系统优化任务;③借助提供的丰富案例资源,拓展在智能优化、状态估计、微电网调度等方向的研究思路与技术手段。; 阅读建议:建议读者结合文档中提供的网盘资源,下载完整代码与工具包,边学习理论边动手实践。重点关注YALMIP工具的使用方法,并通过复现文中提到的多个案例,加深对电力系统优化问题建模与求解的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值