(树形dp)poj 3140 Contestants Division

这篇文章讲解了解决POJ 3140问题的思路,涉及图划分、连通度计算、动态规划,以及如何通过优化数据结构(链式前向星)和避免TLE来求解两部分价值绝对差的最小值。关键步骤包括dp数组表示连通度,以及利用abs函数计算绝对差值。

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

题目
poj3140

题意:
有一张图,可以把其中一条边去掉把图分成两个部分,输出最小的两个部分的值的绝对差。

思路:
记录全图的总价值cnt;
以结点1为根,dp [ i ] 为记录第 i 个结点向下的连通度。
对于其中一个结点 p ,遍历与它有边的结点s1,s2,,,sn,
a n s = min ⁡ i = 1 n ∣ d p [ i ] + v [ i ] − ( c n t − ( d p [ i ] + v [ i ] ) ) ∣ = min ⁡ i = 1 n ∣ ( d p [ i ] + v [ i ] ) ∗ 2 − c n t ∣ ans = \min_{i=1}^{n}|dp[i]+v[i]-(cnt-(dp[i]+v[i]))|=\min_{i=1}^{n}|(dp[i]+v[i])*2-cnt| ans=i=1minndp[i]+v[i](cnt(dp[i]+v[i]))=i=1minn(dp[i]+v[i])2cnt

ps:abs()是int型的,long long 的话要重新写一个关于abs的函数

ps:用vector存图会TLE,要换种结构存图(用链式前向星可以)

ps:删除的是一条边,所有下面这种情况是错误的:
在这里插入图片描述

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector> 
#define DEBUG freopen("_in.txt", "r", stdin); freopen("_out1.txt", "w", stdout);
#define ll long long
using namespace std;
const int MAXN = 1e5 + 10;
const int MAXM = 2e6 + 10;
const ll INF = 9e18;
ll dp[MAXN], v[MAXN];
bool vis[MAXN];
int tree[MAXN], ti;
struct road{
	int v;
	int next;
}R[MAXM];
void addroad(int u, int v){
	R[ti].v = v;
	R[ti].next = tree[u];
	tree[u] = ti++;
	R[ti].v = u;
	R[ti].next = tree[v];
	tree[v] = ti++;
}
void dfs(int p){
	vis[p] = true;
	dp[p] = 0;
	for (int i = tree[p], s; i != -1; i = R[i].next){
		s = R[i].v;
		if (!vis[s]){
			dfs(s);
			dp[p] = dp[p] + dp[s] + v[s]; 
		}
	}
}
ll llabs(ll x){
	return (x < 0) ? -x : x;
}
int main(){
	int n, m, T = 1;
	while (~scanf("%d%d", &n, &m) && !(n == 0 && m == 0)){
		ll cnt = 0;
		for (int i = 1; i <= n; i++){
			scanf("%lld", &v[i]);
			cnt += v[i];
		}
		memset(tree, -1, sizeof(tree));
		ti = 0;
		for (int i = 0, p, s; i < m; i++){
			scanf("%d%d", &p, &s);
			addroad(p, s);
		}
		memset(vis, false, sizeof(vis));
		dfs(1);
		ll ans = INF;
		for (int i = 1; i <= n; i++){
			for (int j = tree[i], s; j != -1; j = R[j].next){
				s = R[j].v;
				ans = min(ans, llabs((dp[s]+v[s]<<1)-cnt));
			}
			if (ans == 0)
				break;
		}
		printf("Case %d: %lld\n", T++, ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值