poj-3140

来源:互联网 发布:写作业软件下载 编辑:程序博客网 时间:2024/05/29 08:24
//10344K    391MS   C++#include <cstdio>#include <cstring>#include <cmath>const long long MAX = 100005;long long adjacentListHead[MAX];long long studentTotalSum;struct Node{    long long studentNum;};long long Idx;struct ConnectedNode{    int curNodeId;    int nextNodePos;};typedef struct ConnectedNode ConnectedNode;ConnectedNode adjacentNodes[1000005];typedef struct Node Node;Node nodes[MAX];long long DP[MAX];long long uNum;long long connectionNum;long long minDiffer = -1;inline long long MIN(long long A, long long B) {    return A < B ? A: B;}void addInAdjacentList(int nodeId, int adjacentNodeId) {    Idx++;    adjacentNodes[Idx].curNodeId = adjacentNodeId;    adjacentNodes[Idx].nextNodePos = adjacentListHead[nodeId];    adjacentListHead[nodeId] = Idx;}inline long long ABS(long long A) {    return A > 0? A: -A;}void dfs(long long curNodeId, long long prevNodeId) {    // visited[curNodeId] = 1;    long long adjacentNodePos = adjacentListHead[curNodeId];    long long adjacentNodeId = adjacentNodes[adjacentNodePos].curNodeId;    long long adjacentNum = 0;    while(adjacentNodeId) {        // prlong longf("A adjacentNodeId: %d curNodeId: %d prevNodeId: %d\n", adjacentNodeId, curNodeId, prevNodeId);        if (adjacentNodeId != prevNodeId) {            dfs(adjacentNodeId, curNodeId);            adjacentNum++;            DP[curNodeId] += DP[adjacentNodeId];        }        adjacentNodePos = adjacentNodes[adjacentNodePos].nextNodePos;        adjacentNodeId = adjacentNodes[adjacentNodePos].curNodeId;    }    DP[curNodeId] += nodes[curNodeId].studentNum;    // prlong longf("%lld %d\n", DP[curNodeId], curNodeId);    if (minDiffer == -1) {        minDiffer = ABS(studentTotalSum - 2*DP[curNodeId]);    } else {         minDiffer = MIN(minDiffer, ABS(studentTotalSum - 2*DP[curNodeId]));    }}long long caseId = 1;int main() {    while(scanf("%lld %lld", &uNum, &connectionNum) != EOF) {        if (!uNum && !connectionNum) {            return 0;        }        Idx = 0;        studentTotalSum = 0;        minDiffer = -1;        memset(DP, 0, sizeof(DP));        // memset(visited, 0, sizeof(visited));        memset(adjacentListHead, 0, sizeof(adjacentListHead));        memset(adjacentNodes, 0, sizeof(adjacentNodes));        for (long long i = 1; i <= uNum; i++) {            scanf("%lld", &(nodes[i].studentNum));            studentTotalSum += nodes[i].studentNum;        }        for (long long i = 1; i <= connectionNum; i++) {            int A, B;            scanf("%d %d", &A, &B);            addInAdjacentList(A, B);            addInAdjacentList(B, A);        }        dfs(1, -1);        printf("Case %lld: %lld\n", caseId, minDiffer);        caseId++;    }}

水题,虽然挂着树状DP的名头,不过是非常简单的运用,倒是因为数据值太大引起了不少RE 和 WA。

题目要求的其实说白了就是,给你一个无根树,然后选择树种中的一条边断开,使得形成的两个子树的权值和的差的绝对值最小。

因为是无根树,所以完全可以任选一个点作为根,而因为题目的输入没有给出两个点的父子关系,并且树的节点非常多,因此在保存该树的时候,要用图的邻接表来表示(好久没用,邻接表的预分配数组开小了,RE了一次),在题目输入的时候,就先累加得到整个树的权值和S, 然后,就是选择一个点作为root(直接选了1),

那么下面就是DP了,DP[i]表示以i为根的子树的权值和,那么很简单的,DP[i] = SUM{DP[i的子节点]}, 注意,因为用的是邻接表表示的树,因此,实际的遍历点i的所有子节点,是通过遍历与i邻接的所有节点实现的,但是在这些邻接的点中,会有一个点其实是i的父节点,这个要区分开,可以在dfs()中多加一个参数P,表示此次dfs的节点i,其父节点是P,这样在遍历i的邻接点时,可以直接将p跳过。

每次得到一个子树的权值和时(即DP[i]),就算一个 abs(S - 2*DP[i])(就是以i为根的子树权值和 与 剩下的点组成的子树的权值和的差的绝对值), 在每次的dfs中,保存最小的abs(S - 2*DP[i]);

最后输出该最小值即可。

0 0