树形DP二次扫描:POJ3585 && CF219D

POJ3585

题解

  • 选择一个点,使它到所有端点的流量和最大。
  • 一棵树,我们可求每个结点到子树的端点的最大流量和。DFS一遍,自低先上。设g[u]表示到u结点到子树端点的最大流量和,那么g[u] += min(g[v],e.cap)。v为root的子节点,e.cap表示这一条边的容量。如果下一个结点使叶子结点,那么直接加上e.cap,即g[u] += e.cap。
  • 然后再DFS一遍,求结点到所有端点的流量和,用f[u]表示。u的父节点为fa,他们中间的表为e,则f[u]肯定包含g[u],往下流的一定在。还有一部分往父亲节点,最大可流e.cap。其值为min(e.cap,f[fa] - min (e.cap,g[u]))。因为是自顶向下,所以fa往所有端点的流量已经求好了。
  • 注意第二遍dfs时,初始化f[1] = g[1],根节点只能玩往下流。
  • 如果根节点只有一个儿子,那么要特判。f[u] = g[u] + e.cap。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
int const N = 200000 + 10;
int n,g[N],f[N];
struct Node
{
	int to,cap;
	Node(){}
	Node(int to,int cap):to(to),cap(cap){}
};
vector<Node>G[N];
void dfs1(int u,int fa){   //自低向上
	for(int i=0;i<G[u].size();i++){
		Node e = G[u][i];
		if(e.to == fa)	continue;
		dfs1(e.to,u);
		if(G[e.to].size() == 1)			g[u] += e.cap;
		else	g[u] += min(e.cap,g[e.to]);
	}
}
void dfs2(int u,int fa){  //自顶向下
	for(int i=0;i<G[u].size();i++){
		Node e = G[u][i];
		if(e.to == fa)	continue;   //叶结点已经直接跳过了
		if(G[u].size() == 1)	f[e.to] = g[e.to] + e.cap;  
		else f[e.to] = g[e.to] + min(e.cap,f[u] - min(e.cap,g[e.to]));
		dfs2(e.to,u);
	}
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		memset(g,0,sizeof(g));
		memset(f,0,sizeof(f));
		for(int i=1;i<=n;i++)	G[i].clear();
		for(int i=1;i<n;i++){
			int u,v,c;
			scanf("%d%d%d",&u,&v,&c);
			G[u].push_back(Node(v,c));
			G[v].push_back(Node(u,c));
		}
		dfs1(1,0);
		f[1] = g[1];
		dfs2(1,0);
		int ans = 0;
		for(int i=1;i<=n;i++)
			ans = max(ans,f[i]);
		cout<<ans<<endl;
	}
	return 0;
}

CF219D

题解

  • 有向树,找一个结点,反转的最少的边,可以到所有的结点。
  • 正向边用1表示,反向边用0表示。那么所求的点满足,经过所有的边的权值和最大。
  • 同上,两次DFS。第一次求点到字数所有结点的权值和g。再反转一次DFS,求到父亲的权值和f。最后就是g + f。

代码

#include <bits/stdc++.h>
using namespace std;
int const N = 200000 + 10;
int n,g[N],f[N];
struct Node
{
	int to,val;
	Node(){}
	Node(int to,int val):to(to),val(val){}
};
struct Node1
{
	int id,v;
	bool operator < (const Node1& e)const{
		return v > e.v || v == e.v && id < e.id;
	}
}ans[N];
vector<Node>G[N];
void dfs(int u,int fa){
	for(int i=0;i<G[u].size();i++){
		Node e = G[u][i];
		if(e.to == fa)	continue;
		dfs(e.to,u);
		g[u] += e.val;
		g[u] += g[e.to];
	}
}
void dfs2(int u,int fa){
	for(int i=0;i<G[u].size();i++){
		Node e = G[u][i];
		if(e.to == fa)	continue;
		f[e.to] = g[e.to] + (f[u] - g[e.to] - e.val) + (e.val ? 0 : 1);
		dfs2(e.to,u);
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n-1;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		G[u].push_back(Node(v,1));
		G[v].push_back(Node(u,0));
	}
	dfs(1,-1);
	f[1] = g[1];
	dfs2(1,-1);
	for(int i=1;i<=n;i++){
		ans[i].id = i;
		ans[i].v = f[i];
	}
	sort(ans+1,ans+1+n);
	int sum = 1;
	for(int i=2;i<=n;i++)
		if(ans[i].v == ans[1].v)	sum++;
	printf("%d\n",n - 1 - ans[1].v);   //总共有n-1条边,不需要修改的为ans[sum].v
	for(int i=1;i<=sum;i++)
		printf("%d%c",ans[i].id,i == sum ? '\n' : ' ');
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值