1. 程式人生 > >[NOIP模擬][拓撲排序][貪心]拆網線

[NOIP模擬][拓撲排序][貪心]拆網線

題目描述
企鵝國的網咖們之間由網線互相連線,形成一棵樹的結構。現在由於冬天到了,供暖部門缺少燃料,於是他們決定去拆一些網線來做燃料。但是現在有 K 只企鵝要上網和別人聯機遊戲,所以他們需要把這 K 只企鵝安排到不同的機房(兩隻企鵝在同一個機房會吵架),然後拆掉一些網線,但是需要保證每隻企鵝至少還能通過留下來的網線和至少另一隻企鵝聯機遊戲。
所以他們想知道,最少需要保留多少根網線?
輸入格式:
第一行一個整數 T ,表示資料組數;
每組資料第一行兩個整數 N,K ,表示總共的機房數目和企鵝數目。
第二行 N-1 個整數,第 i 個整數 Ai 表示機房 i+1 和機房 Ai 有一根網線連線(1≤Ai≤i)。
輸出格式:


每組資料輸出一個整數表示最少保留的網線數目。
樣例輸入:
2
4 4
1 2 3
4 3
1 1 1
樣例輸出:
2
2
資料範圍:
對於 30% 的資料:N≤15;
對於 50% 的資料:N≤300;
對於 70% 的資料:N≤2000;
對於 100% 的資料:2≤K≤N≤100000,T≤10。
題目分析
這道題看了樣例就感覺是貪心。因為求最少保留的網線數目,很明顯那種單獨一塊,一根網線連兩個房間,然後這兩個房間不再用網線與其它的房間相連就是最優的,貢獻是1:2,相當於是最大化的利用了這根網線。所以考慮怎樣從圖中最多的劃分出這樣的一塊一塊,如果劃分出來後的線個數為cnt,如果房間2*cnt大於k,那麼ans就是k/2(k為奇數再加一);如果小於說明這種劃分不能滿足,那麼再加線就是加一根線多一個房間,於是ans=cnt+(k-2*cnt)。最後考慮怎樣計算圖中最多可劃分出多少,方法多樣。可以用拓撲排序,假設1為根,然後做一遍拓撲排序(下附程式碼是用dfs求的)。然後按照拓撲序從大到小,每次判斷這個點和其指向的點是否皆還未被選中,如果沒有被選中,就選中,ans++,k=k-2。
PS:
會拓撲序的請略過······,拓撲排序
附程式碼

#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<algorithm>
using namespace std;

const int N=1e5
+10; int n,k,a,ans,tot,nxt[N*2],first[N],to[N*2],cnt,b[N],t; bool vis[N]; int readint() { char ch;int i=0,f=1; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') {ch=getchar();f=-1;} for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0'; return i*f; } void create(int x,int y) { tot++; nxt[tot]=first[x]; first[x]=tot; to[tot]=y; } void dfs(int u,int fa) { for(int e=first[u];e;e=nxt[e]) { int v=to[e]; if(v!=fa) { dfs(v,u); } } b[++cnt]=u; } int main() { //freopen("tree.in","r",stdin); //freopen("tree.out","w",stdout); t=readint(); while(t--) { memset(first,0,sizeof(first)); memset(vis,0,sizeof(vis)); tot=0;cnt=0;ans=0; n=readint();k=readint(); for(int i=1;i<n;i++) { a=readint(); create(i+1,a); create(a,i+1); } dfs(1,0); for(int i=1;i<=n;i++) { if(k<=1) break; int u=b[i]; if(vis[u]==true) continue; for(int e=first[u];e;e=nxt[e]) { int v=to[e]; if(vis[v]==false) { vis[v]=true; k-=2; ans++; break; } } vis[u]=true; } ans+=k; printf("%d\n",ans); } return 0; }