题目
比赛
题意:
一棵树,删一条边或加一条边的花费都是1.求将这棵树变成以条包含全部点的没有多余边的环的最小花费。
题解:
也就是,先将树分成尽可能少的m条链,然后再把这m条链首尾连起来,花费是2×m-1.
类似于树上最长链的做法,以每个点为根的树,求全部分成链的最少数目b,和去掉一条链的最少数目a。前者可选两棵子树的a加上其它子树的b,后者同理,详见代码。
//Time:1359ms //Memory:59968KB #pragma comment(linker, "/STACK:1024000000,1024000000") #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> using namespace std; #define FI first #define SE second #define MP(x,y) make_pair(x,y) const int MAXN= 2000010; const double EPS = 1e-8; const int INF = 1000000007; int he[MAXN],to[MAXN],nex[MAXN],top; void add(int u,int v) { to[top]=v; nex[top]=he[u]; he[u]=top++; } pair<int,int> dfs(int h,int fa) { int b=0,on=0,tw=0; for(int i=he[h];i!=-1;i=nex[i]) if(fa!=to[i]) { pair<int,int> tmp=dfs(to[i],h); b+=tmp.SE+1; if(on>tmp.FI-tmp.SE-1) tw=on,on=tmp.FI-tmp.SE-1; else if(tw>tmp.FI-tmp.SE-1) tw=tmp.FI-tmp.SE-1; } return MP(b+on,b+on+tw); } int main() { //freopen("/home/moor/Code/input","r",stdin); int n,ncase,a,b; scanf("%d",&ncase); while(ncase--) { scanf("%d",&n); top=0; memset(he,-1,sizeof(he)); for(int i=1;i<n;++i) scanf("%d%d",&a,&b),add(a,b),add(b,a); printf("%d\n",dfs(1,-1).SE*2+1); } return 0; }