紀中暑假集訓 2020.08.07【NOIP提高組】模擬 T2:【佛山市選2013】樹環轉換
阿新 • • 發佈:2020-08-08
【佛山市選2013】樹環轉換
Description
給定一棵N個節點的樹,去掉這棵樹的一條邊需要消耗值1,為這個圖的兩個點加上一條邊也需要消耗值1。樹的節點編號從1開始。在這個問題中,你需要使用最小的消耗值(加邊和刪邊操作)將這棵樹轉化為環,不允許有重邊。
環的定義如下:
(1)該圖有N個點,N條邊。
(2)每個頂點的度數為2。
(3)任意兩點是可達的。
樹的定義如下:
(1)該圖有N個點,N-1條邊。
(2)任意兩點是可達的。
Input
第一行是一個整數N代表節點的個數。
接下來N-1行每行有兩個整數U, V(1 ≤ U, V ≤ N),表示雙向邊(U, V)
Output
輸出把樹轉化為環的最小消耗值。
Sample Input
4
1 2
2 3
2 4
Sample Output
3
Data Constraint
對於20%的資料,有1≤N≤10。
對於100%的資料,有1≤N≤1000000。
反思&題解
比賽思路: 忘了……
正解思路: 我們考慮貪心的思想:去掉多的邊補齊少的邊,很顯然最優肯定是他們一一對應,所以最後結果就是去掉的邊*2再加上樹比環少的那條邊。對於一個葉子節點,我們就不用去掉這條邊了;對於一個節點有兩個及以上個兒子的,就要去掉只剩1條(根節點的話可以剩2條),不過如果它一個兒子被去掉過邊,那這個兒子就不用算了。運用這種思想遍歷一棵樹統計答案就行了;
不過GMOJ的評測機會爆棧,所以要用人工棧或者BFS
(據說樹形DP才是最正的正解,不過貪心能過為啥要DP呢)
反思:
CODE
#include<bits/stdc++.h> using namespace std; struct arr { int to,next; }edge[2000005]; int n,head[2000005],cnt,ans,d[4000005],fa[2000005],tot[2000005]; bool bz[2000005],flag[2000005]; void add(int u,int v) { edge[++cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt; } int bfs() { int h=0,t=1; d[1]=1; bz[1]=true; while (h<t) { int i,u=d[++h]; for (i=head[u];i;i=edge[i].next) { int v=edge[i].to; if (v!=fa[u]) { d[++t]=v; fa[v]=u; } } } int i; for (i=t;i>=1;i--) printf("%d\n",d[i]); for (i=t;i>=1;i--) if (tot[d[i]]<2) tot[fa[d[i]]]++; memset(d,0,sizeof(d)); h=0; t=1; d[1]=1; while (h<t) { int u=d[++h],i; if (tot[u]>=2) { ans+=tot[u]-1; if (u==1) ans--; } for (i=head[u];i;i=edge[i].next) { int v=edge[i].to; if (v!=fa[u]) d[++t]=v; } } } int main() { scanf("%d",&n); int i; for (i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } bfs(); printf("%d\n",ans*2+1); return 0; }