POJ 3140 Contestants Division 树形DP
题意:
有一棵树,有点权,你可以选择一条边删除,要求删除后形成的2棵子树的权值的差的绝对值最小
输出这个最小值
本来是简单题,一个数组siz
siz[i]表示以i为根的子树的节点的权值之和
然后遍历一遍,找到最小值即可。
注意:
0.点权要long long
1.由于点权之和是long long 的,你在求最小值的时候,初始化ret,
要初始化为ret=0x3f3f3f3f3f3f3f3f,而不能是0x3f3f3f3f
2.输入的数据描述的是n m
(1 ≤ N ≤ 100000, 1 ≤ M ≤ 1000000)
n表示节点的个数,m表示边的个数
然后我就呆了,题目明明说了这是一棵树啊,也就是边数为n-1啊,这里弄个m<= 1000000是什么?
然后想想才知道,这是唬人的,数据一定会保证m=n-1的,被骗了,然后还以为自己读错题意了,又看了很久的题目
3.这里的abs()函数要自己写,因为cmath的abs()函数是int abs()的,在这里不适合,会CE
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define ll long long using namespace std; const int maxn=1e5+10; const ll inf=0x3f3f3f3f3f3f3f3f; struct Edge { int to,next; }; Edge edge[maxn<<1]; int tot; int head[maxn]; ll val[maxn]; ll siz[maxn]; ll sum; void init() { memset(head,-1,sizeof head); tot=0; sum=0; } void addedge(int u,int v) { edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } ll solve(int ); int main() { int cas=1; int n,m; while(scanf("%d %d",&n,&m)){ if(!n&&!m) break; printf("Case %d: ",cas++); init(); for(int i=1;i<=n;i++){ scanf("%lld",&val[i]); sum+=val[i]; } for(int i=1;i<=m;i++){ int u,v; scanf("%d %d",&u,&v); addedge(u,v); addedge(v,u); } printf("%lld\n",solve(n)); } return 0; } void dfs(int u,int pre) { siz[u]=val[u]; for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].to; if(v==pre) continue; dfs(v,u); siz[u]+=siz[v]; } } ll solve(int n) { //printf("eee\n"); dfs(1,-1); ll ret=inf; for(int i=2;i<=n;i++){ ll cnt=sum-2*siz[i]; if(cnt<0) cnt*=(-1); if(cnt<ret) ret=cnt; } return ret; }