POJ 1985 Cow Marathon && POJ 1849 Two(树的直径)
树的直径:树上的最长简单路径。
求解的方法是bfs或者dfs。先找任意一点,bfs或者dfs找出离他最远的那个点,那么这个点一定是该树直径的一个端点,记录下该端点,继续bfs或者dfs出来离他最远的一个点,那么这两个点就是他的直径的短点,距离就是路径长度。具体证明见http://www.cnblogs.com/wuyiqi/archive/2012/04/08/2437424.html 其实这个自己画画图也能理解。
POJ 1985
题意:直接让求最长路径。
可以用dfs也可以用bfs
bfs代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; typedef pair<int, int> pii; const int maxn = 200000; int tot, head[maxn]; struct Edge { int to, next, w; }edge[maxn]; bool vis[maxn]; void init() { tot = 0; memset(head, -1, sizeof(head)); } void addedge(int u, int v, int w) { edge[tot].to = v; edge[tot].w = w; edge[tot].next = head[u]; head[u] = tot++; } int maxx, pos; void bfs(int p)//从p开始找到离他最远的那个点,距离保存在maxx当中 { maxx = -1; memset(vis, false, sizeof(vis)); queue<pii> Q; vis[p] = true; pii cur, nex; cur.first = p; cur.second = 0;//pair的first表示节点编号,second表示权值 Q.push(cur); while (!Q.empty()) { cur = Q.front(); Q.pop(); for (int i = head[cur.first]; i != -1; i = edge[i].next) { int v = edge[i].to; if (vis[v]) continue; vis[v] = true; nex.first = v; nex.second = cur.second + edge[i].w; if (maxx < nex.second)//如果找到最大的就替换 { maxx = nex.second; pos = v; } Q.push(nex); } } } int main() { int n, m; while (~scanf("%d %d", &n, &m)) { init(); int u, v, w; for (int i = 0; i < m; i++) { scanf("%d %d %d %*s", &u, &v, &w); addedge(u, v, w); addedge(v, u, w); } bfs(1); bfs(pos); printf("%d\n", maxx); } return 0; }
dfs代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; typedef pair<int, int> pii; const int maxn = 200000; int tot, head[maxn]; struct Edge { int to, next, w; }edge[maxn]; bool vis[maxn]; void init() { tot = 0; memset(head, -1, sizeof(head)); } void addedge(int u, int v, int w) { edge[tot].to = v; edge[tot].w = w; edge[tot].next = head[u]; head[u] = tot++; } int maxx, pos; void dfs(int p, int fa, int w) { for (int i = head[p]; i != -1; i = edge[i].next) { int v = edge[i].to; if (v == fa || vis[v]) continue; vis[v] = true; if (w + edge[i].w > maxx) { maxx = w + edge[i].w; pos = v; } dfs(v, p, w + edge[i].w); vis[v] = false; } } void solve() { memset(vis, false, sizeof(vis)); maxx = -1; dfs(1, 0, 0); maxx = -1; dfs(pos, 0, 0); printf("%d\n", maxx); } int main() { int n, m; while (~scanf("%d %d", &n, &m)) { init(); int u, v, w; for (int i = 0; i < m; i++) { scanf("%d %d %d %*s", &u, &v, &w); addedge(u, v, w); addedge(v, u, w); } solve(); } return 0; }
POJ 1849
题意: 给出一棵树,两个人从给定的点s开始走,走完这棵树最少走的长度。
思路:如果要回到当初的点,根据树的性质,那么一定是将这棵树走了两遍,但是题目要求可以停在任何位置,所以走不到两边,有些边走了一遍,求最小花费,那么一定是最长的那条路径走了一遍,其他都是两遍,这样才是最小花费。仔细想想其实从哪个点开始走并不影响最后的结果。因为不管哪个点肯定都要走完。要使两个人走的简单路径最长,那么这两个人走的路径就是树的最长路径了。所以答案就是:两倍的所有路径权值之和减去树的直径
代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; typedef pair<int, int> pii; const int maxn = 220000; int tot, head[maxn]; struct Edge { int to, next, w; }edge[maxn]; bool vis[maxn]; void init() { tot = 0; memset(head, -1, sizeof(head)); } void addedge(int u, int v, int w) { edge[tot].to = v; edge[tot].w = w; edge[tot].next = head[u]; head[u] = tot++; } int maxx, pos; void bfs(int p) { maxx = -1; memset(vis, false, sizeof(vis)); queue<pii> Q; pii cur, nex; cur.first = p; cur.second = 0; vis[p] = true; Q.push(cur); while (!Q.empty()) { cur = Q.front(); Q.pop(); for (int i = head[cur.first]; i != -1; i = edge[i].next) { int v = edge[i].to; if (vis[v]) continue; vis[v] = true; nex.first = v; nex.second = cur.second + edge[i].w; if (nex.second > maxx) { maxx = nex.second; pos = v; } Q.push(nex); } } } int main() { int n, s; while (~scanf("%d %d", &n, &s)) { init(); int u, v, w, ans = 0; for (int i = 1; i < n; i++) { scanf("%d %d %d", &u, &v, &w); addedge(u, v, w); addedge(v, u, w); ans += w * 2; } bfs(s); bfs(pos); printf("%d\n", ans - maxx); } return 0; }