貪心複習~(好像暴露了什麼算法……)
標籤:貪心 / DFS / Codeforces算法
給出一棵以1爲根的樹,每條邊有兩個值:p-強度、w-重量。
對於給出的樹,咱們能夠對每條邊進行操做——將它的p、w同時減去相同的值,可是要求 \(p\ge0,w>0\) 。(注意只能減,不能加)
進行操做後,須要使原樹知足:若是 u 是 v 的父親,那麼 u 到 v 的邊的 p 不能小於 以 v 爲根節點的子樹中全部邊的 w 之和。
求出一種方案,使得在知足條件的狀況下,樹的 w 之和最大,輸出這個方案。若是不存在任何方案,輸出-1(Of course 有 SPJ)函數
簡單地思考一下,對於這道題,葉子節點的p以及與根節點相連的邊的w是沒有用的……
根據這個咱們能夠想出一個思路——從下到上儘量減去重量,求到重量最小的樹;再從上到下貪心地儘量給每條邊加劇量(不超過原重量),獲得答案。比較有趣的是正解好像也是這麼寫的——由於按理來講樣例的解不少,可是個人程序跑出來是同樣的~spa
從根節點DFS到葉子節點,而後從葉子節點遞歸獲得整個樹的最小重量解(固然是惟一解)
聲明一下變量:code
(1) edg[i] 按照輸入順序給出的第i條邊(從1開始);
(2) fedg[i] 對應edg[i],表示修改後的第i條邊;
(3) pnt[i] 表示以i爲根的子樹的最小總重量(知足條件的);
(4) (u,v) 表示 u,v 之間的邊在輸入時的順序
Tab. edg,fedg 都是結構體,包含元素 wgt,ref 分別表示重量、強度blog
假設如今是 u點,已經計算出它的兒子 v,邊的編號 id=(u,v)。
首先判斷重量是否合法——若是子樹v的最小重量 pnt[v] 都大於 edg[id].ref 了,那麼就不合法,輸出-1。
不然,顯然若是不考慮 \(edg[id].wgt>0\) ,那麼咱們能夠把 edg[id].ref 降至 pnt[v] —— 那麼咱們就是要在 \(edg[id].wgt>0\) 的狀況下儘量的使 edg[id].ref 小。計算 \(delta\) 表示將 edg[id] 的 wgt 和 ref 同時減去 delta。那麼 \(delta=min\{edg[id].ref-hvy\ ,\ edg[id].wgt-1\}\),將削減後的值存入 fedg[id] ,最後統計 pnt[u] 。遞歸
這樣咱們就求出了最小重量的方案(同時判斷了不存在解的狀況)。get
根據最開始的分析,咱們能夠將與根節點相連的全部邊的 wgt 和 ref 都調至最大(也就是初始值)。
在 Solve() 函數中除了 當前節點u 還附帶一個變量 \(del\)表示u的子樹在最小基礎下的可以增長的最大重量。其實Solve()的本質仍是一個 DFS……可是它的返回值是 以u爲根的子樹中在最小重量的基礎上新增長的重量之和,因此咱們用deltot來記錄這個返回值。string
那麼從根節點出發,咱們能夠把 del 看作是正無窮,由於沒有任何限制。
假設如今正在處理 邊(u,v) ,u是父親。
若是當前邊再加上 del 不超過原來的重量,那麼就能夠將它加上,返回 deltot+=del(這裏del就至關於加劇量的機會,而這種狀況就至關於把全部機會用完了)。
不然在給當前邊增長重量後,del還有剩餘——那麼就貪心地把 del 分配到 v 裏去。也就是先給 邊(u,v) 加大到可能的最大值,同時將 del 減去增長的值。可是咱們不能直接將 del 下傳到 v 去,由於可能 v 的子樹在增長 del 的重量後,邊(u,v) 的強度不夠,因此下傳時,咱們進行一個處理—— \(min(del,fedg[id].ref-pnt[v])\)(這裏的pnt[v]其實就是在未對v進行 Solve() 修改時的v的子樹總重)。最後在 Solve() 返回時將 del 減去其返回值,表示用去了這麼多「機會」。it
而後就能夠了~io
/*Lucky_Glass*/ #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=(int)2e5; struct GRAPH{ struct NODE{ int to,nxt,id; NODE(){} NODE(int _to,int _nxt,int _id):to(_to),nxt(_nxt),id(_id){} }nod[N*2+7]; int adj[N+7],cnt,siz[N+7]; GRAPH(){memset(adj,-1,sizeof adj);cnt=0;} void AddEdge(int u,int v,int id,bool dir){ siz[u]++;nod[++cnt]=NODE(v,adj[u],id);adj[u]=cnt; if(!dir) AddEdge(v,u,id,true); } }grp; struct EDGE{ int u,v; long long ref,wgt; }edg[N+7],fedg[N+7]; int n; long long pnt[N+7]; long long DFS(int u,int pre){ long long hvytot=0; for(int i=grp.adj[u];i!=-1;i=grp.nod[i].nxt){ int v=grp.nod[i].to,id=grp.nod[i].id; if(v==pre) continue; long long hvy=DFS(v,u); if(hvy>edg[id].ref){ printf("-1\n"); exit(0); } long long delta=min(edg[id].ref-hvy,edg[id].wgt-1); fedg[id].ref=edg[id].ref-delta; fedg[id].wgt=edg[id].wgt-delta; hvytot+=hvy+fedg[id].wgt; } return pnt[u]=hvytot; } long long Solve(int u,int pre,long long del){ //del:u的子樹在最小基礎下的可以增長的最大重量 long long deltot=0; for(int i=grp.adj[u];i!=-1;i=grp.nod[i].nxt){ int v=grp.nod[i].to,id=grp.nod[i].id; if(v==pre) continue; if(fedg[id].wgt+del<=edg[id].wgt){ fedg[id].wgt+=del;fedg[id].ref+=del; deltot+=del; del=0;break; } else{ deltot+=edg[id].wgt-fedg[id].wgt; del-=edg[id].wgt-fedg[id].wgt; fedg[id]=edg[id]; } long long delv=Solve(v,u,min(del,fedg[id].ref-pnt[v])); del-=delv;deltot+=delv; } return deltot; } int main(){ scanf("%d",&n); for(int i=1;i<n;i++){ scanf("%d%d%d%d",&edg[i].u,&edg[i].v,&edg[i].wgt,&edg[i].ref); fedg[i]=edg[i]; grp.AddEdge(edg[i].u,edg[i].v,i,false); } DFS(1,0); Solve(1,0,(1ll<<60)); printf("%d\n",n); for(int i=1;i<n;i++) printf("%d %d %lld %lld\n",fedg[i].u,fedg[i].v,fedg[i].wgt,fedg[i].ref); return 0; }
- 若是blog裏面有沒講清楚的……能夠在個人郵箱\(lucky\_glass@foxmail.com\)問我~