HDOJ 4582 - DFS spanning tree - DFS树,贪心

题目大意:

给定一个N个点、M条边的无向图Graph,以及从点1开始进行DFS形成的树Tree,定义"T-Simple Circle"为Graph中的环,要求其中只含一条不属于Tree的边。

将Graph中的一些边进行染色,使得其中每个T-simple Circle都至少包含一条被染色的边,求最少需要染色的边数。

N≤2e3,M≤2e4

本题关键的一点在于Tree是一棵DFS生成树,这样Tree以外的边只可能将某个点与它在Tree中的祖先相连(用反证法可以证明,只有这样才能维持DFS树的性质)。

也就是说,每条Tree以外的边都相当于在DFS生成树上划定了一条深度单调递增(递减)的链,问题转化为:最少染色多少条边,可以使每条链上都至少有一条边被染色。

不难发现,对Tree以外的边进行染色的覆盖效率远小于对Tree上的边进行染色,因此只需考虑DFS生成树的边。

类比直线上的区间选点问题,本题也可以用类似的贪心思路。

题解:http://blog.csdn.net/sd_invol/article/details/9963741

直线上区间选点问题的证明:http://blog.csdn.net/dgq8211/article/details/7534776

C++11代码(貌似HDOJ可以交C++11?):

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <vector>
  5 #include <functional>
  6
  7 const int maxN = 2000 + 5;
  8 const int maxM = 20000 + 5;
  9
 10 std::vector<int> toVec[maxN];
 11 int father[maxN]; //father in the DFS tree
 12 int depth[maxN]; //depth in the DFS tree
 13 bool covered[maxN]; //whether the edge (x - father[x]) is covered (used in greedy algorithm)
 14
 15 struct Range
 16 {
 17     int head, tail; //We guarantee that depth[head] >= depth[tail]
 18
 19     void swapEndPoint()
 20     {
 21         if (depth[head] < depth[tail])
 22             std::swap(head, tail);
 23     }
 24     bool operator < (const Range& rhs) const
 25     {
 26         return depth[tail] > depth[rhs.tail] ||
 27                 (depth[tail] == depth[rhs.tail] && depth[head] < depth[rhs.head]);
 28         //high depth -> 0 --- 0 --- 0 --- 0 --- 0 -> low depth
 29         //greater:            x --------- x
 30         //less:         x --------------------- x
 31     }
 32 };
 33
 34 Range range[maxM];
 35 int N, M;
 36
 37 void init()
 38 {
 39     memset(father, 0, sizeof(father));
 40     memset(depth, 0, sizeof(depth));
 41     memset(covered, 0, sizeof(covered));
 42     for (int i = 1; i <= N; i++)
 43         toVec[i].clear();
 44 }
 45
 46 /// @brief swap head and tail so that depth[head] >= depth[tail]
 47 ///        this function is called after depth[] is set, before sorting ranges for greedy algorithm
 48 void initRange()
 49 {
 50     for (int i = 0; i < M - N + 1; i++)
 51         range[i].swapEndPoint();
 52 }
 53
 54 bool input()
 55 {
 56     scanf("%d%d", &N, &M);
 57     if (N == 0)
 58         return false;
 59
 60     init();
 61     for (int u, v, i = 0; i < N - 1; i++) //(N - 1) Edges in DFS tree
 62     {
 63         scanf("%d%d", &u, &v);
 64         toVec[u].push_back(v);
 65         toVec[v].push_back(u);
 66     }
 67     for (int u, v, i = N - 1; i < M; i++)
 68     {
 69         scanf("%d%d", &u, &v);
 70         range[i - N + 1] = {u, v}; //The end points may be swapped later
 71     }
 72
 73     return true;
 74 }
 75
 76 ///@brief DFS process, setting depth[] and father[]
 77 void dfs(int cur, int last)
 78 {
 79     father[cur] = last;
 80     depth[cur] = depth[last] + 1;
 81     for (auto to: toVec[cur])
 82     {
 83         if (to == last)
 84             continue;
 85         dfs(to, cur);
 86     }
 87 }
 88
 89 ///@brief wrapper of DFS function
 90 void setDepthAndFather()
 91 {
 92     depth[0] = 0;
 93     dfs(1, 0);
 94 }
 95
 96 int solve()
 97 {
 98     setDepthAndFather();
 99     initRange();
100     std::sort(range, range + M - N + 1); //(M - N + 1) Edges that does not belong to the DFS tree
101
102     ///@return last if edge (last, father[last]) should be covered
103     ///        0 if no edge should be covered in this chain
104     auto getCoverEdge = [] (const Range& rg) -> int
105     {
106         int last = rg.head;
107
108         //higher depth -> head -> tail -> lower depth
109         for (int cur = rg.head; cur != rg.tail; cur = father[cur])
110         {
111             if (covered[cur])
112                 return 0;
113             last = cur;
114         }
115         return last;
116     };
117
118 //    ///@debug
119 //    for (int i = 1; i <= N; i++)
120 //        printf("father[%d] = %d, depth[%d] = %d\n", i, father[i], i, depth[i]);
121
122     int ans = 0;
123     for (int i = 0; i < M - N + 1; i++)
124     {
125         int coverId = getCoverEdge(range[i]);
126         if (coverId == 0)
127             continue;
128         ans += 1;
129         covered[coverId] = true;
130     }
131
132     return ans;
133 }
134
135 int main()
136 {
137     while (input())
138         printf("%d\n", solve());
139     return 0;
140 }

原文地址:https://www.cnblogs.com/Onlynagesha/p/8448560.html

时间: 2024-10-11 08:17:01

HDOJ 4582 - DFS spanning tree - DFS树,贪心的相关文章

HDU 4582 DFS spanning tree

题意: 一张无向图中告诉你一个dfs树,还有若干反向边.问你如何选取最小的边使得所有只包含一条反向边的环被覆盖. 转化题意,一条不在生成树上的边能构成一个环,假设这条边是 \(u-v\) ,那么就可以看作在dfs生成树上的一条 \(u-v\) 的路径.要求在生成树上选最少的边使得能让每一条路径内都至少有一条选的边 这题有个特点,题目说了这些非树边都是反向边,那就说明了 \(u,v\) 的 lca 一定是 \(u,v\) 中的一个 我们这边设 \(u\) 的深度比 \(v\) 的深度小 很像线段覆

【POJ 3321】 Apple Tree (dfs重标号设区间+树状数组求和)

[POJ 3321] Apple Tree (dfs重标号设区间+树状数组求和) Apple Tree Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 21966   Accepted: 6654 Description There is an apple tree outside of kaka's house. Every autumn, a lot of apples will grow in the tree. K

Codeforces 383C . Propagating tree【树状数组,dfs】

题目大意: 有一棵树,对这个树有两种操作:1:表示为(1 x val),在编号为x的节点上加上val,然后给x节点的每个儿子加上- val,再给每个儿子的儿子加上-(- val),一直加到没有儿子为止.2:表示为(2 x)查询x节点上的值. 做法: 由于每次修改操作修改的并不是一个值,而是很多值,那我们将该题抽象成区间修改,点查询的问题.那怎么抽象呢?可以明白的是,每次操作虽然有加有减,但是每次做加法操作,或者减法操作的都是同一部分数(也就是说,在某次加上同一个数的节点们,下次操作一定是加上或者

【HDOJ 5834】Magic boy Bi Luo with his excited tree(树型DP)

[HDOJ 5834]Magic boy Bi Luo with his excited tree(树型DP) Magic boy Bi Luo with his excited tree Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Problem Description Bi Luo is a magic boy, he also has a migic tree,

[BZOJ 1103][POI 2007]大都市(DFS求拓扑序+树状数组)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1103 题目大意:给你一个树,刚开始所有树边边权均为1,不断地将其中的某些边边权改为0,其间问你某个点到根节点之间路径上的边权和. 此题和POJ的Apple Tree很相近... 首先DFS生成整棵树的拓扑序,DFS时每个结点i进入的时间l[i]和离开的时间r[i],然后对每次更改操作,维护树状数组即可. #include <iostream> #include <stdi

hdu 2489 Minimal Ratio Tree(dfs枚举 + 最小生成树)~~~

题目: 链接:点击打开链接 题意: 输入n个点,要求选m个点满足连接m个点的m-1条边权值和sum与点的权值和ans使得sum/ans最小,并输出所选的m个点,如果有多种情况就选第一个点最小的,如果第一个点也相同就选第二个点最小的........ 思路: 求一个图中的一颗子树,使得Sum(edge weight)/Sum(point weight)最小~ 数据量小,暴力枚举~~~~~dfs暴力枚举C(M,N)种情况. 枚举出这M个点之后,Sum(point weight)固定,进行prim或者K

hdoj 1045 Fire Net 【DFS】

题意:如果两个点要放在同一行或者同一列,那么两个点中间要有一个墙,否则的话只能放一个点,最后问你最多能放几个点. 看了一个星期.. 这道题的解法我还是第一次见,就是逐个逐个的来放置每个点,然后每经过一个点都判断一次,详情看代码 代码: #include <stdio.h> #include <string.h> int ans, n; char map[10][10]; int judge(int lin, int row) { int i; for(i = lin-1; i &g

HDOJ 题目4408 Minimum Spanning Tree(Kruskal+Matrix_Tree)

Minimum Spanning Tree Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1408    Accepted Submission(s): 450 Problem Description XXX is very interested in algorithm. After learning the Prim algori

图的DFS。。类似树的DFS

Depth-First Search (DFS) Depth-first search (DFS) is an algorithm for traversing or searching tree or graph data structures. One starts at the root (selecting some arbitrary node as the root in the case of a graph) and explores as far as possible alo