贪心复习~(好像暴露了什么算法……)
标签:贪心 / 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\)问我~