题目链接:
http://codeforces.com/contest/911/problem/F
题目大意:
给你一棵树,每次挑选这棵树的两个度数为1的点,加上他们之间的边数(距离),然后将其中一个点去掉,问你边数(距离)之和最大可以是多少.
要求输出每次选择的两个点和删掉的点
题解:
贪心题,策略是每次选择一个度数为1的点和距离它较远的直径的端点(显然直径的端点度数为1),然后把这个点删掉保留直径的端点。直到只剩下直径的时候才开始删掉直径上的点
为什么?考虑到先删一个直径外的点再删直径的端点一定不会比删掉一个直径的端点再删这个点更劣(不妨画个图?)。
应该还有这么个结论吧,距离任意一个点最远的叶子节点一定是直径的一个端点,感觉挺正确的
#include<algorithm> #include<cstring> #include<cstdio> #include<iostream> #include<vector> #include<queue> using namespace std; typedef long long ll; const int N=2e5+15; int n; int d1[N],d2[N],deg[N],vis[N],a[N],b[N],c[N]; vector <int> g[N]; inline int read() { char ch=getchar(); int s=0,f=1; while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} return s*f; } void dfs(int x,int fa,int *d) { for (int i=0;i<g[x].size();i++) { int y=g[x][i]; if (y==fa) continue; d[y]=d[x]+1; dfs(y,x,d); } } void out(int x,int fa,int goal) { for (int i=0;i<g[x].size();i++) { int y=g[x][i]; if (vis[y]||y==fa) continue; printf("%d %d %d ",x,goal,x); out(y,x,goal); } } int main() { n=read(); for (int i=1,u,v;i<n;i++) { u=read();v=read(); g[u].push_back(v);g[v].push_back(u); deg[u]++;deg[v]++; } int p1,p2,mx=0; d1[1]=0;dfs(1,-1,d1); for (int i=1;i<=n;i++) if (d1[i]>mx) {mx=d1[i];p1=i;} d1[p1]=0;dfs(p1,-1,d1); mx=0;for (int i=1;i<=n;i++) if (d1[i]>mx) {mx=d1[i];p2=i;} d2[p2]=0;dfs(p2,-1,d2); queue<int> q; ll ans=0; int tot=0; for (int i=1;i<=n;i++) if (d1[i]+d2[i]!=d1[p2]&°[i]==1) q.push(i); while (!q.empty()) { int k=q.front();q.pop(); ans+=max(d1[k],d2[k]); a[++tot]=k;b[tot]=(d1[k]>d2[k])?p1:p2;c[tot]=k;vis[k]=1; for (int i=0;i<g[k].size();i++) { int y=g[k][i]; if (vis[y]) continue; --deg[y]; if (d1[y]+d2[y]!=d1[p2]&°[y]==1) q.push(y); } } ans+=1ll*(d1[p2]+1)*d1[p2]/2; printf("%lld ",ans); for (int i=1;i<=tot;i++) printf("%d %d %d ",a[i],b[i],c[i]); out(p1,-1,p2); return 0; }