http://acm.hdu.edu.cn/showproblem.php?pid=2586
题意:给你一个带权值的无向图,求其中两点的最小距离。
思路:这题貌似别的方法也行,这里还是为了练习LCA用这办法。不同于LCA求最小公共祖先的值,由于输入的是无向边,所以首先输入边时记得反向同步添加一条。接着我们可以把这个图看成一个以1为根的树,遍历LCA,当全部遍历完后,遍历需要查询的点。由于是无向边,顺序也就不重要了,通常在回溯返回过程中只要遇到搜索的点,他的前一个节点必然访问过。因为要查询的无向边中,回溯过程中处理的都是晚搜索的点,而这里由于反向输入建立了等价关系(不怎么好表达,懂了就好,自己模拟一下就知道了)。回溯过程返回的自然都是根到当前节点的举例,这里求两点距离时只要他们两个节点分别到根的距离减去最小公共祖先到根距离的两倍就行。对了这里有个i^1有个小技巧,与1异或代表求和这个点相搭配点的下标,应该在处理无向图问题中很常见。
忘了乘2WA了几次,这数据也太弱了,不乘2都看不出来。还有最后那个"Output a bland line after each test case."是几个意思= =你妹
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 100010;
const int INF = 0x3f3f3f3f;
int head[N], head0[N], dist[N], pre[N];
int n, m, pos, pos0;
bool vis[N];
struct node
{
int from, to, w, next;
}edge[N];
struct node0
{
int from, to, w, next;
}query[N];
void add(int u, int v, int w)
{
edge[pos] = (struct node){u, v, w, head[u]};
head[u] = pos++;
edge[pos] = (struct node){v, u, w, head[v]};
head[v] = pos++;
}
void add0(int u, int v)
{
query[pos0] = (struct node0){u, v, 0, head0[u]};
head0[u] = pos0++;
query[pos0] = (struct node0){v, u, 0, head0[v]};
head0[v] = pos0++;
}
void init()
{
pos = pos0 = 0;
memset(edge, 0, sizeof(edge));
memset(query, 0, sizeof(query));
memset(vis, false, sizeof(vis));
memset(pre, 0, sizeof(pre));
memset(dist, 0, sizeof(dist));
memset(head, -1, sizeof(head));
memset(head0, -1, sizeof(head0));
}
int Find(int u)
{
if(pre[u] == u) return u;
pre[u] = Find(pre[u]);
return pre[u];
}
void LCA(int u)
{
pre[u] = u;
vis[u] = true;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(!vis[v])
{
dist[v] = dist[u]+edge[i].w;
LCA(v);
pre[v] = u;
}
}
for(int i = head0[u]; i != -1; i = query[i].next)
{
int v = query[i].to;
if(vis[v])
{
query[i].w = dist[u]+dist[v]-dist[Find(v)]*2;
query[i^1].w = query[i].w;
}
}
}
int main()
{
// freopen("in.txt", "r", stdin);
int t, u, v, w;
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &m);
init();
for(int i = 1; i < n; i++)
{
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
}
for(int i = 1; i <= m; i++)
{
scanf("%d%d", &u, &v);
add0(u, v);
}
LCA(1);
for(int i = 0; i < pos0; i+=2)
{
printf("%d\n", query[i].w);
}
// printf("\n");
}
return 0;
}
http://poj.org/problem?id=1986
题意思路基本同上,多输了个方向和顶点数并没有什么卵用。
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 100010;
const int INF = 0x3f3f3f3f;
int head[N], head0[N], dist[N], pre[N];
int n, m, pos, pos0;
bool vis[N];
struct node
{
int from, to, w, next;
}edge[N];
struct node0
{
int from, to, w, next;
}query[N];
void add(int u, int v, int w)
{
edge[pos] = (struct node){u, v, w, head[u]};
head[u] = pos++;
edge[pos] = (struct node){v, u, w, head[v]};
head[v] = pos++;
}
void add0(int u, int v)
{
query[pos0] = (struct node0){u, v, 0, head0[u]};
head0[u] = pos0++;
query[pos0] = (struct node0){v, u, 0, head0[v]};
head0[v] = pos0++;
}
void init()
{
pos = pos0 = 0;
memset(edge, 0, sizeof(edge));
memset(query, 0, sizeof(query));
memset(vis, false, sizeof(vis));
memset(pre, 0, sizeof(pre));
memset(dist, 0, sizeof(dist));
memset(head, -1, sizeof(head));
memset(head0, -1, sizeof(head0));
}
int Find(int u)
{
if(pre[u] == u) return u;
pre[u] = Find(pre[u]);
return pre[u];
}
void LCA(int u)
{
pre[u] = u;
vis[u] = true;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(!vis[v])
{
dist[v] = dist[u]+edge[i].w;
LCA(v);
pre[v] = u;
}
}
for(int i = head0[u]; i != -1; i = query[i].next)
{
int v = query[i].to;
if(vis[v])
{
query[i].w = dist[u]+dist[v]-dist[Find(v)]*2;
query[i^1].w = query[i].w;
}
}
}
int main()
{
// freopen("in.txt", "r", stdin);
int nv, u, v, w;
char s[5];
while(~scanf("%d%d", &nv, &n))
{
init();
for(int i = 1; i <= n; i++)
{
scanf("%d%d%d%s", &u, &v, &w, s);
add(u, v, w);
}
scanf("%d", &m);
for(int i = 1; i <= m; i++)
{
scanf("%d%d", &u, &v);
add0(u, v);
}
LCA(1);
for(int i = 0; i < pos0; i+=2)
{
printf("%d\n", query[i].w);
}
printf("\n");
}
return 0;
}