• P1453 城市环路 题解


    P1453 城市环路 题解

    间隙

    前置知识

    • 树形dp,基环树

    大致题意

    给一颗含有点权的基环外向树

    假如两个点之间有一条边连接,如果选择了其中一端的节点,那另一段的节点则不可选择

    求:最大贡献

    分析

    先讲一下什么是基环树。

    基环树,简单来说就是多了一条边的树,产生了一个环形结构,环上的每个节点都是一颗树的根

    画成图的话大概是这个样子(基环外向树)

    一般来说,这种题目的做法都是先找到环,断开环中的一条边,
    把它当成一般的树形(DP)来做。

    如何找环?

    一般有(dfs)跟并查集两种方法 , 这里我采用的是并查集的做法

    一开始每个节点都是一个独立的集合

    每连接一条边,就把这两个点合并到一个集合中

    如果在连接一条边之前,两个节点就已经在一个集合中了,说明这两个节点已经联通了,再连接这条边必然会产生环的情况

    如何转移?

    找到了环之后,只需要将环上的这条边断开即可

    这样的话就可以当作普通的树形(DP)来做了

    (f[i][0])为选第(i)个节点产生的最大贡献

    (f[i][1])为不选第(i)个节点产生的最大贡献

    如果选了第(i)个节点,那它的儿子肯定都不能选

    反之,儿子可以选择选,也可以选择不选

    得到转移方程:

    (f[u][0] = sum f[v][0])

    (f[u][1] = sum max(f[v][1],f[v][0]))

    代码实现

    思路明白了代码实现应该就不难了

    要注意的是环上的两个点都可以作为树的根节点,因此在(DP)的时候要把两个点都跑一遍

    具体的细节注释有写

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN = 1e5+10;
    struct edge{//存图 
    	int v,next;
    }e[MAXN<<1];
    int f[MAXN][2],w[MAXN];//dp数组,点权 
    double k; 
    int fa[MAXN];
    int head[MAXN<<1],cnt = 0;
    int root1,root2;//环上的两个点 
    int n;
    void add(int u,int v){//前向星 
    	e[++cnt].v = v;
    	e[cnt].next = head[u];
    	head[u] = cnt;
    } 
    int find(int x){//查找集合 
    	if(fa[x]==x){
    		return x;
    	}
    	else{
    		return fa[x] = find(fa[x]);
    	}
    }
    
    void circle(int u,int fa){//树形dp 
    	f[u][1] = w[u],f[u][0] = 0;//初始化 
    	for(int i=head[u];i;i=e[i].next){
    		int v = e[i].v;
    		if(v!=fa){ 
    			circle(v,u);
    			f[u][0]+=max(f[v][1],f[v][0]);//转移 
    			f[u][1]+=f[v][0];
    		}
    	}
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&w[i]);
    		fa[i] = i;//初始化集合 
    	}
    	for(int i=1;i<=n;i++){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		u++,v++;
    		if(find(u)==find(v)){//如果在加边前就在一个集合中了,说明找到了环 
            	root1 = u,root2 = v;//记录环上的两个点 
            	continue;//直接跳过加边操作,相当于断开这条边 
    		}
            add(u,v);
    		add(v,u);
    		fa[find(v)] = find(u);//合并集合 
    	}
    	scanf("%lf",&k);
    	circle(root1,0);
    	double r1 = f[root1][0];//选root1 
    	
    	circle(root2,0);
    	double r2 = f[root2][0];//选root2 
    	
    	printf("%.1lf",max(r1,r2)*k);//取最大 
    	return 0;
    }
    
  • 相关阅读:
    JSP中 == 和equals的区别
    使用Cookie保存用户名密码,再次登陆时将Cookie用户名密码取出来并直接放置到用户名密码文本框中
    学习Java Web开发中遇到的问题,及其解决方法
    部署、测试、服务工作的经验记录
    Python基础--dict字典操作
    Python基础--dict字典
    Python基础--预留空 5
    Python基础--预留空 4
    Python基础--tuple 元组
    Python基础--预留3
  • 原文地址:https://www.cnblogs.com/xcxc82/p/13429884.html
Copyright © 2020-2023  润新知