20寒假ACM训练第三天

今天训练的主要内容是最短路与最小生成树的问题。
最短路,基于图的最短路,今天做的好像没有负边。主要有三种算法(今天没有讲bellman ford,讲了我再补上)。
第一种:Floyd(佛洛依德)
优势:可以获得任意两点之间的最短路径。
劣势:耗费时间太长时间复杂度n^3,1s的话n大概200~500危险。Floyd不能用于有负权环的情况。(好像可以用于判断是否有负权环,遇见了再说吧)

void floyd{
for(int k=0;k<n;k++)
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			a[i][j]=min(a[i][k]+a[k][j],a[i][j]);
}

简单好记。
第二种和第三种都用于单源路径(即某点到其他所有点的最短距离)
第二种:SPFA
优势:相对于第三种,能够用于有负边的情况。
劣势:时间复杂度在O(m+n)到O(m*n)之间,不稳定。
void spfa{
queue Q;
for(int i=1;i<=n;i++){
dis[i]=INF;//INF不懂的可以去看我的另一篇笔记;
vis[i]=0;
}
dis[1]=0;
vis[0]=1;
Q.push(1);
while(!Q.empty()){
int u=Q.front();Q.pop();visp[u]=0;
for(int i=h[u];i;i=e[i].next){
int v=e[i].v;
if(dis[u]+e[i].c dis[v]=dis[u]+e[i].c;
if(!vis[v]){
vis[v]=1;
Q.push(v);
}
}
}
}
第三种:dijkstra(迪结斯特拉)
优势:稳定:时间复杂度在O(mlogm),其实细想应该最糟糕的情况是n^2longm,但是取决于边的数目,对于绝大多数情况,边的数目是很少的(即使是稠密图),因此时间复杂度为mlongm.

pair<int,int>pii;
struct Edge{
	int to,dist;
};
vector<Edge>G[M];
int d[N],done[N];
void dijkstra(void s)//s为出发的节点
	priority_queue<pii,vector<pii>,greater<pii> >Q;
	for(int i=1;i<N;i++)d[i]=(i==s?0:INF);
	memset(done,0,sizeof(done));
	Q.push(0,s);//注意这里先存距离,后存节点编号,因为我们采用优先队列,pair类型定义为先比较第一个值,相同情况下比较第二个值。
	while(!Q.empty()){
		pii x=Q.top();Q.pop();
		int u=x.second;
		if(done(u))continue;
		done(u)=1;
		for(int i=0;i<G[u].size();i++){
			Edge& e=G[u][i];
			if(e.dist+d[u]]<d[e.to]){
				d[e.to]=e.dist+d[u];
				Q.push(d[e.to],e.to);
			}
	}	
}

这里提一下数据输入的时候的方法.由于是pair型变量的储存,用一个临时的pair

for(int i=0;i<n;i++){
	int a,b,c;
	cin>>a>>b>>c;
	Edge temp;
	temp.to=b;
	temp.dist=c;
	G[a].push(temp);
}

我们说着这种用于单源路径,事实上,有些题目是要我们求其他所有点到某个点的最短路径,而此时又是有向图,此时我们的方法是将路径

反向保存一次,用反向保存的路径求出该点到其他点的最短距离,从而等价的求出了其他点到的该点的最短距离。

举个栗子:
Silver Cow Party
One cow from each of N farms (1 ≤ N ≤ 1000) conveniently numbered 1…N is going to attend the big cow party to be held at farm #X (1 ≤ X ≤ N). A total of M (1 ≤ M ≤ 100,000) unidirectional (one-way roads connects pairs of farms; road i requires Ti (1 ≤ Ti ≤ 100) units of time to traverse.
Each cow must walk to the party and, when the party is over, return to her farm. Each cow is lazy and thus picks an optimal route with the shortest time. A cow’s return route might be different from her original route to the party since roads are one-way.
Of all the cows, what is the longest amount of time a cow must spend walking to the party and back?
Input
Line 1: Three space-separated integers, respectively: N, M, and X
Lines 2… M+1: Line i+1 describes road i with three space-separated integers: Ai, Bi, and Ti. The described road runs from farm Ai to farm Bi, requiring Ti time units to traverse.
Output
Line 1: One integer: the maximum of time any one cow must walk.
Sample Input
4 8 2
1 2 4
1 3 2
1 4 7
2 1 1
2 3 5
3 1 2
3 4 4
4 2 3
Sample Output
10
Hint
Cow 4 proceeds directly to the party (3 units) and returns via farms 1 and 3 (7 units), for a total of 10 time units.

简单读题后我们发现我们需要求某点到其他所有点最短路径与对应点到该点路径之和的最小值。于是就可以正反存两次路径,求两次即可。
我用的是dijkstra
AC代码如下:

#include 
#include 
#include 
#include 
#include 
#define N 1005
#define M 100005
#define INF 0x3f3f3f3f
using namespace std;
int d[N],d1[N],done[N],done1[N];
struct edge{
	int to,dist;
};
vector<edge> G[N],G1[N];
typedef pair<int,int> pii;
priority_queue<pii,vector<pii>,greater<pii> >Q,Q1;
void dijkstra(int s){
	for(int i=1;i<N;i++)d[i]=(i==s?0:INF);
	memset(done,0,sizeof(done));
	Q.push(pii(0,s));
	while(!Q.empty()){
		pii x=Q.top();Q.pop();
		int u=x.second;
		if(done[u])continue;
		done[u]=1;
		for(int i=0;i<G[u].size();i++){
			edge a=G[u][i];
			if(a.dist+d[u]<d[a.to]){
				d[a.to]=a.dist+d[u];
				Q.push(pii(d[a.to],a.to));
			}
		}
	}
}
void dijkstra1(int s){
	for(int i=1;i<N;i++)d1[i]=(i==s?0:INF);
	memset(done1,0,sizeof(done1));
	Q1.push(pii(0,s));
	while(!Q1.empty()){
		pii x=Q1.top();Q1.pop();
		int u=x.second;
		if(done1[u])continue;
		done1[u]=1;
		for(int i=0;i<G1[u].size();i++){
			edge a=G1[u][i];
			if(a.dist+d1[u]<d1[a.to]){
				d1[a.to]=a.dist+d1[u];
				Q1.push(pii(d1[a.to],a.to));
			}
		}
	}
}
int main(){
	int n,m,x;
	cin>>n>>m>>x;
	for(int i=1;i<=m;i++){
		int a,b,c;
		cin>>a>>b>>c;
		edge temp;
		temp.dist=c,temp.to=b;
		G[a].push_back(temp);
		edge temp1;
		temp1.dist=c,temp1.to=a;
		G1[b].push_back(temp1);
	}
	dijkstra(x);
	dijkstra1(x);
	int ans=1;
	for(int i=2;i<=n;i++)
		if(d[ans]+d1[ans]<d[i]+d1[i])ans=i;
	cout<<d[ans]+d1[ans]<<endl;
	return 0;
}

最小生成树
今天只讲了kruskal算法,其实就是通过并查集判断两点是否已经连通,然后用cost从小到大遍历即可,很简单不多说。
并查集在我另一篇笔记里面有,就不写了。题也很简单,就不多说。
大概就是这样。

你可能感兴趣的:(20ACM寒假)