刷題總結——疫情控制(NOIP2012提高組)
題目:
題目背景
NOIP2012 提高組 DAY2 試題。
題目描述
H 國有 n 個城市,這 n 個城市用 n-1 條雙向道路相互連通構成一棵樹,1 號城市是首都,也是樹中的根節點。
H 國的首都爆發了一種危害性極高的傳染病。當局為了控制疫情,不讓疫情擴散到邊境城市(葉子節點所表示的城市),決定動用軍隊在一些城市建立檢查點,使得從首都到邊境城市的每一條路徑上都至少有一個檢查點,邊境城市也可以建立檢查點。但特別要註意的是,首都是不能建立檢查點的。
現在,在 H 國的一些城市中已經駐紮有軍隊,且一個城市可以駐紮多個軍隊。一支軍隊可以在有道路連接的城市間移動,並在除首都以外的任意一個城市建立檢查點,且只能在一個城市建立檢查點。一支軍隊經過一條道路從一個城市移動到另一個城市所需要的時間等於道路的長度(單位:小時)。
請問最少需要多少個小時才能控制疫情。註意:不同的軍隊可以同時移動。
輸入格式
第一行一個整數 n,表示城市個數。
接下來的 n-1 行,每行 3 個整數,u、v、w,每兩個整數之間用一個空格隔開,表示從城市 u 到城市 v 有一條長為 w 的道路。數據保證輸入的是一棵樹,且根節點編號為 1。
接下來一行一個整數 m,表示軍隊個數。
接下來一行 m 個整數,每兩個整數之間用一個空格隔開,分別表示這 m 個軍隊所駐紮的城市的編號。
輸出格式
共一行,包含一個整數,表示控制疫情所需要的最少時間。如果無法控制疫情則輸出-1。
樣例數據 1
輸入 [復制]
4
1 2 1
1 3 2
3 4 3
2
2 2
輸出
3
備註
【樣例說明】
第一支軍隊在 2 號點設立檢查點,第二支軍隊從 2 號點移動到 3 號點設立檢查點,所需時間為 3 個小時。
【數據範圍】
保證軍隊不會駐紮在首都。
對於 20% 的數據,2≤n≤10;
對於 40% 的數據,2≤n≤50,0<w<105;
對於 60% 的數據,2≤n≤1000,0<w<106;
對於 80% 的數據,2≤n≤10,000;
對於 100% 的數據,2≤m≤n≤50,000,0<w<109。
題解:
貪心的妙用····表示一道題先根據題意找到基本策略真的很重要
首先要明白,將軍隊在不超時的情況下往根節點移動越多控制的葉子節點肯定是越多的。
因此先二分答案,然後在答案的限制下盡量將所有軍隊往根節點移動(因為一個軍隊越靠近根節點控制的葉節點肯定越多)
然後通過遞歸(註意這裏遞歸是如果一個節點的所有兒子都被軍隊占領了,相當於它一個節點被占領,這樣遞歸從葉子節點到根節點層層染色)找到根節點(1號)的哪些兒子還需要軍隊來占領(因為如果根節點的兒子還未被占領,則兒子所在子樹的葉子節點肯定還未被控制)
於是那些移動得到根節點的軍隊就有用處了···首先,如果一個軍隊在移動到根節點的過程中經過了這些還未被占領的兒子節點中的一個,則用這個軍隊占領這個兒子節點即可.如果不是,將它們派去占領其他兒子節點(註意這裏又會用到一次貪心,將移動到根節點後剩余時間少的軍隊盡量去占領那些與根節點距離小的兒子節點,總體下來無疑會占領得更多)。
這樣的話所算出的占領葉子節點數一定是最多的
沒想到這道題考貪心會考這麽深···
代碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<algorithm> using namespace std; const int N=5e4+5; int Rint() { char c; int f=0; for(c=getchar();c<‘0‘||c>‘9‘;c=getchar()); for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f<<3)+(f<<1)+c-‘0‘; return f; } long long Rlong() { char c; long long f=0; for(c=getchar();c<‘0‘||c>‘9‘;c=getchar()); for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f<<3)+(f<<1)+c-‘0‘; return f; } struct node { int point; long long dis; }army[N],leftcity[N]; bool visit[N]; bool comp(node x,node y) { return x.dis<y.dis; } int first[N],next[N*2],go[N*2],tot,a[N]; long long val[N*2],dis[N][32]; int n,m,g[N][32],deep[N],tota,totl; void comb(int a,int b,long long c) { next[++tot]=first[a],first[a]=tot,go[tot]=b,val[tot]=c; next[++tot]=first[b],first[b]=tot,go[tot]=a,val[tot]=c; } inline void dfs(int u,int fa) { for(int e=first[u];e;e=next[e]) { int v=go[e]; if(v==fa) continue; g[v][0]=u; deep[v]=deep[u]+1; dis[v][0]=val[e]; dfs(v,u); } } inline void color(int u) { int t1=1,t2=0; for(int e=first[u];e;e=next[e]) { int v=go[e]; if(v==g[u][0]) continue; color(v); if(!visit[v]) t1=0; else t2=1; } if(t1==1&&t2==1&&u!=1) visit[u]=true; } inline bool check(long long limit) { memset(visit,false,sizeof(visit)); tota=0,totl=0; for(int i=1;i<=m;i++) { long long temp=limit; int u=a[i]; for(int j=30;j>=0;j--) { if(g[u][j]>=1&&temp>=dis[u][j]) { temp-=dis[u][j]; u=g[u][j]; if(u==1) break; } } if(u!=1) visit[u]=true; else { army[++tota].dis=temp; u=a[i]; for(int j=30;j>=0;j--) if(g[u][j]>1) u=g[u][j]; army[tota].point=u; } } color(1); for(int e=first[1];e;e=next[e]) { if(!visit[go[e]]) { leftcity[++totl].point=go[e]; leftcity[totl].dis=val[e]; } } sort(leftcity+1,leftcity+totl+1,comp); sort(army+1,army+tota+1,comp); leftcity[totl+1].dis=1e+15; int head=1; for(int i=1;i<=tota;i++) { if(!visit[army[i].point]) visit[army[i].point]=true; else { if(army[i].dis>=leftcity[head].dis) visit[leftcity[head].point]=true; } while(visit[leftcity[head].point]==true) { head++; if(head>totl) return true; } } return false; } int main() { //freopen("a.in","r",stdin); n=Rint(); int A,B; long long C; long long left=0,right=0; for(int i=1;i<n;i++) { A=Rint(),B=Rint(),C=Rlong(); comb(A,B,C); right+=C; } m=Rint(); dfs(1,0); for(int i=1;i<=m;i++) a[i]=Rint(); for(int i=1;i<=30;i++) for(int j=1;j<=n;j++) { g[j][i]=g[g[j][i-1]][i-1]; dis[j][i]=dis[j][i-1]+dis[g[j][i-1]][i-1]; } while(left<right) { long long mid=(left+right)/2; if(check(mid)) right=mid; else left=mid+1; } if(check(left)) cout<<left<<endl; else cout<<"-1"<<endl; return 0; }
刷題總結——疫情控制(NOIP2012提高組)