砍树

砍树

原题链接
给定一棵包含 n 个节点的树。
你的任务是从树中删除尽可能多的边,使得剩余图形的所有连通分量都具有偶数个节点。

输入格式

第一行包含整数 n。
接下来 \(n−1\) 行,每行包含两个整数 a,b,表示节点 a 和 b 之间存在一条边。
节点编号 \(1∼n\)
保证给出图形是一棵树。

输出格式

输出一个整数表示可以删除的最大可能边数。
如果无论如何都不可能使得剩余图形的所有连通分量都具有偶数个节点,则输出 −1。

数据范围

前六个测试点满足,\(1≤n≤10\)
所有测试点满足,\(1≤n≤10^5\)\(1≤a, b≤n\)

题意解读与思路

我们把题细细读下来,可以发现所有连通分量都具有偶数个节点这个条件是非常的严格的,如果总节点数为奇数无论怎么分割都不可能达成题目要求。那么我们开始思考如果有偶数个节点的处理方式。
首先,我们选一个边,假设这条边的左边或者右边有任意一边节点数不为偶数个,因为所有连通分量都要具有偶数个节点,所以显然这条边是无法删除的。接下来我们来思考假设两边有偶数个节点,那么这条边显然是能够被删除的。如果我们不删除这条边,那么在找到下一条边可以被删除的时候这条边还是处于一个偶数个节点的连通分量中,那么这条边还是可以删除,所以删除这条边可以增加删除的边数,可以更靠近最优解。那么我们只要把所有两端具有偶数个节点的边都删除,即使最优解。
那么我们只需要考虑怎么找到这些边就行了,因为只有总数具有偶数个点才满足条件,那么对于每个点假如它的子树有偶数个点,那它和子树连接的这条边就能够删除。所以我们只需要采用dfs就可以解决这个问题。

#include <iostream>
#include <cstring>
using namespace std;

const int N = 1e6 + 10;

int h[N], e[N], ne[N], idx;
int ans = 0;

void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
int dfs(int u, int father){
  int cnt = 1;
  for(int i = h[u]; ~i; i = ne[i]){
    int j = e[i];
    if(j == father)continue;
    int sum = dfs(j, u);
    if(!(sum & 1))ans++;
    else cnt += sum;
  }
  return cnt;
}
int main(){
  int n;
  scanf("%d", &n);
  memset(h, -1, sizeof h);
  if(n & 1){
    puts("-1");
    return 0;
  }
  for(int i = 1; i < n; i++){
    int a, b;
    scanf("%d %d", &a, &b);
    add(a, b), add(b, a);
  }
  dfs(1, -1);
  printf("%d\n", ans);
  return 0;
}
posted @ 2021-09-26 12:57  JOKE_MAKE  阅读(210)  评论(0)    收藏  举报