应该说删点类型的题目比较直观,因为dfs的时候都是从点考虑的多。
这题是要删一条边使得剩余的两个分支 价值差最小。
也是做的第一题删边,一开始没底,但是仔细观察一下还是比较简单的。
发现可以这样理解:删一个点和它父亲之间的这条边(因为父亲只有一个)。想到这里(这也不难想)就好办了,只要想办法记录每个节点 从它子树和父亲各能收获多少价值,它们的差值便是删除它和它父亲这条边的结果
最后遍历一遍比较出最小差值就可以了。
具体第一遍dfs,记录子树有多少节点
第二遍dfs,能从父亲转移多少节点,(这里稍微注意一下 = 父亲的父亲+ 父亲的其它孩子)
【代码】
/* ***********************************************
Author :angon
************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define showtime fprintf(stderr,"time = %.15f\n",clock() / (double)CLOCKS_PER_SEC)
#define lld %I64d
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scanl(d) scanf("%I64d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define scannl(n,m) scanf("%I64d%I64d",&n,&m)
#define mst(a,k) memset(a,k,sizeof(a))
#define LL __int64
#define N 100005
#define mod 1000000007
#define Rabs(a) ((a)<0?-(a):(a))
#define min(a,b) (a)<(b)?(a):(b)
inline int read(){int s=0;char ch=getchar();for(; ch<'0'||ch>'9'; ch=getchar());for(; ch>='0'&&ch<='9'; ch=getchar())s=s*10+ch-'0';return s;}
struct Edge
{
int v,next;
}edge[N*2];
int head[N],tot;
void addedge(int u,int v)
{
edge[tot].v=v; edge[tot].next=head[u]; head[u]=tot++;
}
LL val[N],dp1[N],dp2[N];
void dfs1(int u,int fa)
{
dp1[u] = val[u];
for(int i=head[u]; ~i; i=edge[i].next)
{
int v = edge[i].v;
if(v==fa) continue;
dfs1(v,u);
dp1[u] += dp1[v];
}
}
void dfs2(int u,int fa)
{
for(int i=head[u]; ~i; i=edge[i].next)
{
int v = edge[i].v;
if(v==fa) continue;
dp2[v] += dp2[u];
dp2[v] += dp1[u] - dp1[v];
dfs2(v,u);
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n,m,cas=1;
while(~scann(n,m) && (n||m))
{
REPP(i,1,n) scanl(val[i]);
mst(head,-1); tot=0;
REP(i,0,m)
{
int u,v; scann(u,v);
addedge(u,v);
addedge(v,u);
}
mst(dp2,0);
dfs1(1,-1);
dfs2(1,-1);
LL minc = 1e15;
for(int i=1;i<=n;i++)
minc = min(minc, Rabs(dp1[i] - dp2[i]));
printf("Case %d: %I64d\n",cas++,minc);
}
return 0;
}
【另】
A完之后看了下题解发现有更巧妙的办法,只需第一遍dfs,然后比较每个节点
ans = min(ans,abs((2 * dp[u] - sum)));
sum - 2倍的子树总节点,即删点该节点上面那条边后两个分支的差值。