bfs,dfs,图论

https://www.luogu.com.cn/problem/P1113

题意:给你n件任务,每个任务都有需要的时间,而且做该项任务当且仅当之前的一些任务完成,求最短时间。
思路:首先这些任务是分层的,有顺序之分,想到拓扑排序,然后我的思路是标记出每一层的起点和终点,每一层求出最大时间并累加,在实现的过程中发现标记不好打,看了题解的思路,有一点dp的味道,就是其实每项任务在同一层中是相互独立的,只与其前置任务的时间有关,然后ans是所有任务完成时间的最大值,
代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 10005
#define maxm 10000005
int ver[maxm],head[maxn],ne[maxm],endd[maxn],tim[maxn],in[maxn];
queue<int>st;
int tot=0;
void add(int x,int y){
	ver[++tot]=y;ne[tot]=head[x];head[x]=tot;
}
int n;
void SCAN()
{
	scanf("%d",&n);
	int i;
	for(i=1; i<=n; i++)
	{
		int a,c=1;
		scanf("%d",&a);
		scanf("%d",&tim[a]);
		scanf("%d",&c);
		while(c)
		{
			add(c,a);
			scanf("%d",&c);
		}
	}
}
int main(){
	memset(in,0,sizeof in);

SCAN();


int ans=0;
for(int i=0;i<10005;i++)if(in[i]==0)st.push(i);
while(!st.empty()){
	int now=st.front();st.pop();endd[now]+=tim[now];
	ans=max(ans,endd[now]);
	
	for(int i=head[now];i;i=ne[i]){
		int y=ver[i];
		in[y]--;
		endd[y]=max(endd[y],endd[now]);
		if(in[y]==0)st.push(y);
	}
	
}
cout<<ans<<endl;
return 0;} 

2.spfa怎么死了
做洛谷最短路例题时候,有一道弱化数据版本和一道强化数据版本,这一看就是卡我们可爱的spfa的啊,

https://www.luogu.com.cn/problem/P4779

加强版用dijkstra过了,然鹅用spfa却死了,得了32分,ac了两个点t了4个。
其实我一直感觉dijkstra和spfa算法思路差不多,下面贴一下二者代码:

//dijkstra

#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define maxn 100100
#define maxm 500010
ll inf=pow(2,31)-1;
ll d[maxn],head[maxn],ne[maxm],visited[maxn],ver[maxm],edge[maxm];
int tot=0;
priority_queue<pair<int,int> >q;
void add(int x,int y,int z){
	ver[++tot]=y;edge[tot]=z;
	ne[tot]=head[x];head[x]=tot;
} 
void dijkstra(int p){
	for(int i=0;i<maxn;i++)d[i]=inf;
	memset(visited,0,sizeof visited);
	d[p]=0;
	q.push(make_pair(0,p));
	while(q.size()){
		int now=q.top().second;q.pop();
		if(visited[now])continue;
		visited[now]=1;
		for(int i=head[now];i;i=ne[i]){
			int y=ver[i],z=edge[i];
			if(d[y]>d[now]+z){
			d[y]=d[now]+z;
			q.push(make_pair(-d[y],y));}
		}
	}
}
int main(){
	int n,m,s;
	cin>>n>>m>>s;
	while(m--){
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);
	}
	//cout<<inf<<endl; 
	dijkstra(s);
	for(int i=1;i<=n;i++)cout<<d[i]<<' ';cout<<endl;
return 0;}

spfa

#include<bits/stdc++.h>
#define ll long long 
#define maxn 100010
#define maxm 500010
using namespace std;
ll d[maxn],head[maxn],ne[maxm],visit[maxn],ver[maxm],edge[maxm];
int tot=0;
queue<int>q;
void add(int x,int y,int z){
	ver[++tot]=y;edge[tot]=z;
	ne[tot]=head[x];head[x]=tot;
} 
void spfa(int s){
	memset(d,0x3f,sizeof(d));
	memset(visit,0,sizeof visit);
	d[s]=0;visit[s]=1;
	q.push(s);
	while(q.size()){
		int x=q.front();q.pop();
		visit[x]=0;
		for(int i=head[x];i;i=ne[i]){
			int y=ver[i],z=edge[i];
			if(d[y]>d[x]+z){
				d[y]=d[x]+z;
				if(!visit[y]){
					q.push(y);visit[y]=1;
				}
			}
		}
	}
}
int main(){
	int n,m,s;
	cin>>n>>m>>s;
	while(m--){
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);
	}
	spfa(s);
	for(int i=1;i<=n;i++)cout<<d[i]<<' ';
return 0;}

二者最大区别是dijkstra是对点操作,spfa是对边操作(队列优化的bellman-ford)
蓝书上是这样写的:在任意时刻,该算法的队列都保存了待拓展的节点,每次入队相当于完成一次dist数组的更新操作,十七满足三角形不等式。一个节点可能会入队,出队多次,最终,图中节点收
敛到全部满足三角形不等式的状态。这个队列避免了bellman-Ford算法中对不需要拓展的节点的冗余
扫描,在随机图上运行效率为o(km)(k为小常数),但在随机构造(特意设计)的图上,该算法很可能退化为o(nm),必须谨慎使用

所以,哪些图可以卡掉我们可爱的spfa呢?

posted @ 2022-03-19 17:32  misasteria  阅读(22)  评论(0编辑  收藏  举报