Topic Description
A State-owned n cities, numbered from 1 to n, have m two-way roads between them. Each road has a weight limit on vehicles, referred to as the weight limit. Now there are q trucks carrying goods. Drivers want to know that each truck can carry up to multiple loads without exceeding the vehicle weight limit.
Input Description
In the first line, there are two integers n and m separated by a space, which denote n cities and m roads of state-owned A.
Next, line m has three integers x, y and z in each row, separated by a space between each two integers, indicating that there is a road with a limited weight of z from city x to city y. Note: x is not equal to y. There may be many roads between the two cities.
The next line has an integer q, which means that there are Q trucks that need to be shipped.
Next line q, two integers x and Y in each line, separated by a space, indicates that a truck needs to transport goods from city x to city y. Note: x is not equal to y.
Output Description
The output has q rows, an integer for each row, indicating the maximum load for each truck. If the truck cannot reach its destination, output - 1.
Sample Input
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
Sample Output
3
-1
3
Data Scope and Tips Data Size & Hint
For 30% of the data, 0 < n < 1,000, 0 < m < 10,000, 0 < Q < 1,000;
For 60% of the data, 0 < n < 1,000, 0 < m < 50,000, 0 < Q < 1,000;
For 100% data, 0 < n < 10,000, 0 < m < 50,000, 0 < Q < 30,000, 0 < z < 100,000.
Solution: First, kruskal constructs the maximum spanning tree, and then, for two given points in the tree, finds the shortest line segment between the two points.
Method of realization:
One (60 points), build an array of weight [], and weight[x] records the shortest of all the edges on the way to node X. For each query, run SPFA in the maximum spanning tree and update the value of weight[i] (something like the idea of dp).
However, there is another problem, because the edges of the tree are undirected (bi-directional). When point x is updated, y updates X in turn. This leads to the problem that the weight value of each point is the minimum length of the edge, so it's good to open a bool visited [] array and let each point go only once (it looks like crap, but it's really the mistake I've made).
But TLE has three points.
The code is as follows:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
bool b[10010],visited[10010];//visited each point only once
int n,m,x,y,z,q,x1,yy,num_edge1,num_edge2,head[10010],father[10010],weight[10010];//Maximum weight [] to the city
struct Edge1{
int next,to,dis;
};
Edge1 edge1[50010];
struct Edge2{//Kruskal's edge
int from,to,value;
};
Edge2 edge2[50010];
void add_edge1(int from,int to,int dis)//Graph with Maximum Spanning Tree
{
edge1[++num_edge1].next=head[from];
edge1[num_edge1].to=to;
edge1[num_edge1].dis=dis;
head[from]=num_edge1;
}
void add_edge2(int from,int to,int value)//Kruskal's graph
{
edge2[++num_edge2].from=from;
edge2[num_edge2].to=to;
edge2[num_edge2].value=value;
}
int find(int x)
{
if (father[x]!=x) return father[x]=find(father[x]);
return father[x];
}
void unionn(int x,int y)
{
int xx=find(x);
int yy=find(y);
if (xx!=yy) father[xx]=yy;
}
bool comp(Edge2 a,Edge2 b)
{
return a.value>b.value;//Maximum spanning tree
}
void SPFA(int s,int t)
{
queue<int> q;
memset(b,false,sizeof(b));
memset(visited,false,sizeof(visited));
memset(weight,0x7f,sizeof(weight)); //Initialize to maximum
q.push(s); b[s]=true; //weight[s]=0;
do
{
int now=q.front();
q.pop(); b[now]=false; visited[now]=true;
for (int i=head[now]; i!=0; i=edge1[i].next)
if (!visited[edge1[i].to]&&weight[edge1[i].to]>min(edge1[i].dis,weight[now]))//Error: if (weight [edge 1 [i]. to] > edge 1 [i]. dis)
{
visited[edge1[i].to]=true;
weight[edge1[i].to]=min(edge1[i].dis,weight[now]);//The Idea of Dynamic Programming
if (!b[edge1[i].to])
{
b[edge1[i].to]=true;
q.push(edge1[i].to);
}
}
}
while(!q.empty());
if (weight[t]==0x7f7f7f7f) printf("-1\n");
else printf("%d\n",weight[t]);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++)
father[i]=i;
for (int i=1; i<=m; i++)
{
scanf("%d%d%d",&x,&y,&z);
add_edge2(x,y,z);
}
sort(edge2+1,edge2+1+m,comp);
for (int i=1; i<=m; i++)
{
if (find(edge2[i].from)!=find(edge2[i].to))
{
unionn(edge2[i].from,edge2[i].to);
add_edge1(edge2[i].from,edge2[i].to,edge2[i].value);//Constructing Maximum Spanning Tree
add_edge1(edge2[i].to,edge2[i].from,edge2[i].value);//Undirected graph
}
}
scanf("%d",&q);
for (int i=1; i<=q; i++)
{
scanf("%d%d",&x1,&yy);
SPFA(x1,yy);
}
return 0;
}
It's easy to think of LCA (the recent public ancestor) when querying the line segment between two points.
The first step is to do a dfs memory search (it can be said that the essence of memory search is dynamic programming: optimization principle and post-invalidity), which is used to initialize the information of each point. Then the lca board, an ash function, queries the smallest line segment from one point to the ancestor. Finally, the smaller values of ask in x and y are AC s.
The code is as follows:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
bool b[10010],visited[10010];//visited each point only once
int n,m,x,y,z,q,num_edge1,num_edge2,head[10010],deep[10010],father[10010],fa[10010][17],dis[10010][17];
//fa[x][i]:x node jumps up 2^i node, fa[x][0]:x node's parent node; dis[x][i]: node x jumps up 2^i path's minimum edge weight
struct Edge1{
int next,to,dis;
};
Edge1 edge1[50010];
struct Edge2{//Kruskal's edge
int from,to,value;
};
Edge2 edge2[50010];
void add_edge1(int from,int to,int dis)//Graph with Maximum Spanning Tree
{
edge1[++num_edge1].next=head[from];
edge1[num_edge1].to=to;
edge1[num_edge1].dis=dis;
head[from]=num_edge1;
}
void add_edge2(int from,int to,int value)//Kruskal's graph
{
edge2[++num_edge2].from=from;
edge2[num_edge2].to=to;
edge2[num_edge2].value=value;
}
int find(int x)
{
if (father[x]!=x) return father[x]=find(father[x]);
return father[x];
}
void unionn(int x,int y)
{
int xx=find(x);
int yy=find(y);
if (xx!=yy) father[xx]=yy;
}
bool comp(Edge2 a,Edge2 b)
{
return a.value>b.value;//Maximum spanning tree
}
void dfs(int x)//Memorized search, which saves deep[] fa[][] dis [] [] for each point, is used later.
{
visited[x]=1;
for (int i=1; i<=16; i++)//Initialization
{ //2^16=65536>50000
if ((1<<i)>deep[x]) break;//1<<i <=> 2^i
fa[x][i]=fa[fa[x][i-1]][i-1];//(2^j-1)+(2^j-1)=2^j
dis[x][i]=min(dis[x][i-1],dis[fa[x][i-1]][i-1]);//Error: dis [x] [i] = dis [fa [x] [i-1] [i-1], should be a smaller value ah ah ah ah
}
for (int i=head[x]; i!=0; i=edge1[i].next)
{//Update 3 points
if (!visited[edge1[i].to])//Must not pass, missed this point!!
{
deep[edge1[i].to]=deep[x]+1;
fa[edge1[i].to][0]=x;//Parent node
dis[edge1[i].to][0]=edge1[i].dis;//Store dis[j][0] at the edge of point J
dfs(edge1[i].to);//No backtracking is required
}
}
}
int lca(int x,int y)//Seeking the nearest public ancestor
{
if (deep[x]<deep[y]) swap(x,y);//Exchange subscript
int d=deep[x]-deep[y];//Depth difference
for (int i=0; i<=16; i++)//Pour? Right?
{//Starting at 0 is for the same x and y depths.
if ((1<<i)&d) x=fa[x][i];//Let x jump to the same depth as y (d is depth difference)
}
for (int i=16; i>=0; i--)//To find the deepest common ancestor, look backwards.
if (fa[x][i]!=fa[y][i])
{//Looking for the ancestors of ancestors
x=fa[x][i];//x and y jump up together
y=fa[y][i];
}
if (x==y) return x;//The coincidence of x and y
return fa[x][0];//If x and y do not coincide, x and y have the same father.
}
int ask(int x,int k)//Query the smallest line segment from a point to an ancestor
{
int minn=0x7fffffff;
int d=deep[x]-deep[k];//Distance to ancestors
for (int i=16; i >= 0; i--)
if ((1<<i)<=d)
{
minn=min(minn,dis[x][i]);
x=fa[x][i];//Jump up
d -= (1 << i);
}
return minn;
}
int main()
{
memset(dis,0x7f/3,sizeof(dis));//There's a big problem with uninitialization
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++)
father[i]=i;
for (int i=1; i<=m; i++)
{
scanf("%d%d%d",&x,&y,&z);
add_edge2(x,y,z);
}
sort(edge2+1,edge2+1+m,comp);
int k=0;
for (int i=1; i<=m; i++)
{
if (find(edge2[i].from)!=find(edge2[i].to))
{
unionn(edge2[i].from,edge2[i].to);
add_edge1(edge2[i].from,edge2[i].to,edge2[i].value);//Constructing Maximum Spanning Tree
add_edge1(edge2[i].to,edge2[i].from,edge2[i].value);//Undirected graph
k++;
if (k==n-1) break;
}
}
for (int i=1; i<=n; i++)
if (!visited[i]) dfs(i);
scanf("%d",&q);
for (int i=1; i<=q; i++)
{
int x,y;
scanf("%d%d",&x,&y);
if (find(x)!=find(y)) printf("-1\n");//It's not in one and look-up set. This method is better than==0x7f7f7f7fprintf-1.
else
{
int grandfather=lca(x,y);//Ancestors
printf("%d\n",min(ask(x,grandfather),ask(y,grandfather)));
}
}
return 0;
}
Summary: The key point of this topic is to construct the maximum spanning tree and optimize it with lca multiplier. Sometimes spanning trees are not the whole problem, but only part of it.