题目
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=1minn∣dp[i]+v[i]−(cnt−(dp[i]+v[i]))∣=i=1minn∣(dp[i]+v[i])∗2−cnt∣
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;
}