与SPFA的那些事【二】:进阶题目

本文探讨了SPFA算法在复杂场景下的应用,包括结合二分查找、Floyd算法及图论技巧解决路径寻优问题,通过代码示例详细解析算法实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面给出的主要是灵活运用SPFA的例子,与其他算法相结合,可能会有或多或少的难度。

进阶题目1:Lg P1462 通往奥格瑞玛的道路SPFA+二分答案【提高】

#include <bits/stdc++.h>
#define inf 1000000001
using namespace std;
const int maxn=10001;
const int maxm=100001;

struct EDGE
{
	long long u,v,w,nxt;
}edge[maxm];
long long size=0,n,m,b,l,r,ans;
long long dis[maxn],tot[maxn],head[maxn],f[maxn],used[maxn];
void add(long long u,long long v,long long w)
{
	edge[size].u=u;
	edge[size].v=v;
	edge[size].w=w;
	edge[size].nxt=head[u];
	head[u]=size++;
}


inline long long read()
{
    int x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}

void spfa(long long mid)
{
	deque<int>q;
	for(int i=1;i<=maxn-1;i++)dis[i]=inf,used[i]=0,tot[i]=0;
	q.push_back(1);
	dis[1]=0,used[1]=1,tot[1]=1;
	while(!q.empty())
	{
		int u=q.front();q.pop_front();
		used[u]=0;
		for(long long i=head[u];~i;i=edge[i].nxt)
		{

			long long v=edge[i].v,w=edge[i].w;
			if(f[v]<=mid && dis[v]>dis[u]+w)
			{
				dis[v]=dis[u]+w;
				if(!used[v])
				{
					used[v]=1;
					if(!q.empty())
					{
						if(dis[v]<dis[q.front()])q.push_front(v);
						else q.push_back(v);
					}
					else q.push_back(v);
				}
			}
		}
	}
}

bool judge(long long mid)
{
	spfa(mid);
    if(dis[n]>b) return false;
    return true;
}

void init()
{
	freopen("Lg P1462.in","r",stdin);
}

void readdata()
{
	memset(head,-1,sizeof(head));
	n=read(),m=read(),b=read();
	l=-1000000000,r=1000000000;
    for(int i=1;i<=n;i++) f[i]=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),w=read();
		add(u,v,w);
		add(v,u,w);
	}
}

void work()
{
	spfa(r);
	if(dis[n]==inf || dis[n]>b)	printf("AFK"),exit(0);
	while(l<=r)
	{
		long long mid=(l+r)>>1;
		if(judge(mid))
		{
			r=mid-1;
		}
		else
		{
			l=mid+1;
		}
	}
	printf("%lld",l);
}
int main()
{
	init();
	readdata();
	work();
	return 0;
}

进阶题目2:MZOJ1385: 小奇回地球:floyd+SPFA+二分答案【提高+】

大致思路:

1.二分答案Δdelta,并且每次判断
2.用一次floyd求出两点之间是否连通
3.SPFA+双端队列+判定负环
4.一个优化:用抽屉原理:具体见代码
用时:84MS

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define inf 1e9
using namespace std;
const int maxn=200;
const int maxm=maxn*maxn;
int t,n,m,l,r,ans;
struct EDGE
{
	int u,v,w,nxt;
}edge[maxm];
int size=0;
int head[maxn],vis[maxn][maxn],dis[maxn],used[maxn],tot[maxn];

void add(int u,int v,int w)
{
	edge[size].u=u;
	edge[size].v=v;
	edge[size].w=w;
	edge[size].nxt=head[u];
	head[u]=size++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}

inline void floyd()
{
    for(int i=1;i<=n;i++)vis[i][i]=1;
    for(int k=1;k<=n;k++)
      for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        vis[i][j]=vis[i][j]|(vis[i][k]&vis[k][j]);
         
}

void init()
{
	freopen("MZOJ1385.in","r",stdin);
}

inline void initdata()
{
    size=0;
    for(int i=1;i<=maxn-1;i++)
    head[i]=-1,used[i]=0,tot[i]=0;
    memset(edge,sizeof(edge),0);
    memset(vis,sizeof(vis),0);
}

inline bool spfa(int mid)
{
    deque<int>q;
    for(int i=1;i<=maxn-1;i++)
    dis[i]=inf,used[i]=0,tot[i]=0;
    dis[1]=0;
    tot[1]=1;
    q.push_back(1);
    used[1]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop_front();
        used[u]=0;
        for(int i=head[u];~i;i=edge[i].nxt)
        {
            int v=edge[i].v,w=edge[i].w+mid;
            if(dis[v]>dis[u]+w && vis[v][n])
            {
                dis[v]=dis[u]+w;
                tot[v]++;
                if(tot[v]>n){return false;}
                if(!used[v])
                {
					if(!q.empty())
					{
						if(dis[v]<dis[q.front()])q.push_front(v);
						else q.push_back(v); 
					}
					else q.push_back(v); 
					used[v]=1;
                }
            }
        }
    }
    return true;
}

bool judge(int mid)
{
	if(!spfa(mid))return false;
	if(dis[n]<0)return false;
	if(dis[n]==inf)return false;
	return true;
}

void readdata()
{
	t=read();
    while(t--)
    {
        n=read(),m=read();
        initdata();
        for(int i=1;i<=m;i++)
        {
            int u=read(),v=read(),w=read();
            add(u,v,w);
            vis[u][v]=1;
        }
        floyd();
        if(!vis[1][n])
        {
            printf("-1\n");
            continue;
        }
        l=-1000000,r=1000000;
        ans=-1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(judge(mid))
			{
				r=mid-1,ans=dis[n];
			}
            else l=mid+1;
        }
        printf("%d\n",ans);
    }
}

int main()
{
	init();
	readdata();
	return 0;
}

进阶题目3:P3275 [SCOI2011]糖果:SPFA+建图【提高+】

大致思路:

建图方式很特别
在这里插入图片描述
在这里插入图片描述
via. 浅色调

#include <bits/stdc++.h>
#define inf 1e15+7
using namespace std;
const int maxm=1000000;
const int maxn=100005;
struct EDGE
{
	int u,v,w,nxt;
}edge[maxm];
int n,k,size=0;
int head[maxn];
long long dis[maxn],used[maxn],tot[maxn];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
void add(int u,int v,int w)
{
	edge[size].u=u;
	edge[size].v=v;
	edge[size].w=w;
	edge[size].nxt=head[u];
	head[u]=size++;
}

void spfa()
{
	deque<int>q;
	q.push_back(0);
	dis[0]=0,used[0]=1;
	while(!q.empty())
	{
		int u=q.front();q.pop_front();
		used[u]=0;
		if(tot[u]==n-1){printf("-1"),exit(0);}
		tot[u]++;
		for(int i=head[u];~i;i=edge[i].nxt)
		{
			int v=edge[i].v,w=edge[i].w;
			if(dis[v]<dis[u]+w)
			{
				dis[v]=dis[u]+w;
				if(!used[v])
				{
					used[v]=1;
					if(!q.empty())
					{
						if(dis[v]<dis[q.front()]) q.push_front(v);
						else q.push_back(v); 
					}
					else q.push_back(v); 
				
			//	q.push(v);
				}
			}
		}
	}
}

void init()
{
	freopen("Lg P3275.in","r",stdin);
}

void readdata()
{
	memset(head,-1,sizeof(head));
	n=read(),k=read();
	for(int i=1;i<=k;i++)
	{
		int flag=read(),u=read(),v=read();
		if(flag==1)add(u,v,0),add(v,u,0);
		if(flag==2)
		{
			if(u==v){printf("-1");exit(0);}
			add(u,v,1);
		}
		if(flag==3)add(v,u,0);
		if(flag==4)
		{
			if(u==v){printf("-1");exit(0);}
			add(v,u,1);
		}
		if(flag==5)add(u,v,0);
	}
	for(int i=n;i>=1;i--)add(0,i,1);
}

void work()
{
	spfa();
	long long ans=0;
	for(int i=0;i<=n;i++)
	{
		ans+=dis[i];
	}
	printf("%lld",ans);
}

int main()
{
	init();
	readdata();
	work();
	return 0;
}

进阶题目4:MZOJ1389行动!行动!:SPFA+分类讨论【提高】

我们设dis(i,j)表示i节点用了j个药包的最小代价
分当前点用药包和当前点不用药包讨论
下面给出三组代码
代码1:

SPFA+优先队列优化
#include <bits/stdc++.h>
#define inf 1e8+7
using namespace std;
const int maxm=1000005;
const int maxn=100005;
struct EDGE
{
    int u,v,w,nxt;
}edge[maxm];
int n,m,k,s,e,size;
int head[maxn],dis[maxn][15],used[maxn];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
 
void add(int u,int v,int w)
{
    edge[size].u=u;
    edge[size].v=v;
    edge[size].w=w;
    edge[size].nxt=head[u];
    head[u]=size++;
}
 
struct node
{
    int city,used;
    node():city(0),used(0){}
    node(int a,int b):city(a),used(b){}
    bool operator <(node x)const
    {
        return dis[city][0]>dis[x.city][0];
    }
};
 
void SPFA(int s)
{
    priority_queue<node> q;
    for(int i=1;i<=n;i++)for(int j=0;j<=k;j++)dis[i][j]=inf;
    q.push(node(s,0));
    for(int i=0;i<=k;i++)dis[s][i]=0;
    while(!q.empty())
    {
        node u=q.top();
        q.pop();
        for(int i=head[u.city];~i;i=edge[i].nxt)
        {
            int v=edge[i].v,w=edge[i].w;
            if(dis[v][u.used]>dis[u.city][u.used]+w)
            {
                dis[v][u.used]=dis[u.city][u.used]+w;
                q.push(node(v,u.used));
            }
            if(u.used<k && dis[v][u.used+1]>dis[u.city][u.used])
            {
                dis[v][u.used+1]=dis[u.city][u.used];
                q.push(node(v,u.used+1));
            }
        }
    }
}
 
void init()
{
    freopen("A.in","r",stdin);
}
 
void readdata()
{
    memset(head,-1,sizeof(head));
    n=read(),m=read(),k=read(),s=read()+1,e=read()+1;
    for(int i=1;i<=m;i++)
    {
        int u=read()+1,v=read()+1,w=read();
        add(u,v,w);
        add(v,u,w);
    }
}
 
void work()
{
    SPFA(s);
    int ans=inf;
    for(int i=0;i<=k;++i)
    {
        ans=min(ans,dis[e][i]);
    }
    printf("%d\n",ans);
} 
 
int main()
{
    //init();
    readdata();
    work();
    return 0;
}
/**************************************************************
    Problem: 1389
    User: mzg1811
    Language: C++
    Result: 正确
    Time:136 ms
    Memory:23992 kb
****************************************************************/

注意重载运算符,将优先队列设为小根堆的语法
代码2:

SPFA+双端队列优化
#include <bits/stdc++.h>
#define inf 1e8+7
using namespace std;
const int maxm=1000005;
const int maxn=100005;
struct EDGE
{
    int u,v,w,nxt;
}edge[maxm];
int n,m,k,s,e,size;
int head[maxn],dis[maxn][15],used[maxn];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
 
void add(int u,int v,int w)
{
    edge[size].u=u;
    edge[size].v=v;
    edge[size].w=w;
    edge[size].nxt=head[u];
    head[u]=size++;
}
 
struct node
{
    int city,used;
    node():city(0),used(0){}
    node(int a,int b):city(a),used(b){}
};
 
void SPFA(int s)
{
    deque<node>q;
    for(int i=1;i<=n;i++)for(int j=0;j<=k;j++)dis[i][j]=inf;
     
    q.push_back(node(s,0));
    for(int i=0;i<=k;i++)dis[s][i]=0;
    while(!q.empty())
    {
        node u=q.front();
        q.pop_front();
        for(int i=head[u.city];~i;i=edge[i].nxt)
        {
            int v=edge[i].v,w=edge[i].w;
            if(dis[v][u.used]>dis[u.city][u.used]+w)
            {
                dis[v][u.used]=dis[u.city][u.used]+w;
                if(!q.empty())
                {
                    node x=q.front();
                    if(dis[v][u.used]<dis[x.city][x.used])q.push_front(node(v,u.used));
                    else q.push_back(node(v,u.used));
                }
                else q.push_back(node(v,u.used));
 
            }
            if(u.used+1<=k && dis[v][u.used+1]>dis[u.city][u.used])
            {
                dis[v][u.used+1]=dis[u.city][u.used];
                if(!q.empty())
                {
                    node x=q.front();
                    if(dis[v][u.used+1]<dis[x.city][x.used])q.push_front(node(v,u.used+1));
                    else q.push_back(node(v,u.used+1));
                }
                else q.push_back(node(v,u.used+1));
            }
        }
    }
}
 
void init()
{
    freopen("A.in","r",stdin);
}
 
void readdata()
{
    memset(head,-1,sizeof(head));
    n=read(),m=read(),k=read(),s=read()+1,e=read()+1;
    for(int i=1;i<=m;i++)
    {
        int u=read()+1,v=read()+1,w=read();
        add(u,v,w);
        add(v,u,w);
    }
}
 
void work()
{
    SPFA(s);
    int ans=inf;
    for(int i=0;i<=k;++i)
    {
        ans=min(ans,dis[e][i]);
    }
    printf("%d\n",ans);
} 
 
int main()
{
    //init();
    readdata();
    work();
    return 0;
}
/**************************************************************
    Problem: 1389
    User: mzg1811
    Language: C++
    Result: 正确
    Time:276 ms
    Memory:23992 kb
****************************************************************/

代码3:

dijkstra
#include <bits/stdc++.h>
using namespace std;
 
const int INF=1e9+7;
const int MAXN=100005;
const int MAXM=100005;
const int MAXK=15;
int n,m,k,s,t,ans=INF;
int dis[MAXN][MAXK];
bool used[MAXN][MAXK];
 
int size=0;
int head[MAXN];
struct Edge
{
    int u;
    int v;
    int w;
    int next;
}edge[MAXM<<1];
 
struct node
{
    int city;
    int dis;
    int bag;
    bool operator <(const node &x) const
    {
        return dis>x.dis;
    }
};
 
inline int read()
{
    int X=0; bool flag=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
    while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
    if(flag) return X;
    return ~(X-1);
}
 
inline void add(int u,int v,int w)
{
    edge[size].u=u;
    edge[size].v=v;
    edge[size].w=w;
    edge[size].next=head[u];
    head[u]=size++;
}
 
void readdata()
{
    memset(head,-1,sizeof(head));
    n=read(); m=read(); k=read(); s=read(); t=read();
    for(int i=1;i<=m;i++) 
    {
        int u=read(), v=read(), w=read();
        add(u,v,w);
        add(v,u,w);
    }
}
 
priority_queue <node> q;
void dijkstra()
{
    for(int i=0;i<n;i++) 
        for(int j=0;j<=k;j++) 
        {
            dis[i][j]=INF;
            used[i][k]=0;
        }
    dis[s][0]=0;
    q.push((node){s,0,0});
    while(!q.empty())
    {
        node now=q.top(); q.pop();
        int u=now.city,bag=now.bag;
        if(used[u][bag]) continue;
        used[u][bag]=1;
        for(int i=head[u];~i;i=edge[i].next)
        {
            int v=edge[i].v;
            if(dis[v][bag]>dis[u][bag]+edge[i].w)
            {
                dis[v][bag]=dis[u][bag]+edge[i].w;
                q.push((node){v,dis[v][bag],bag});
            }
            if(bag<k && dis[v][bag+1]>dis[u][bag])
            {
                dis[v][bag+1]=dis[u][bag];
                q.push((node){v,dis[v][bag+1],bag+1});
            }
        }
    }
}
 
void work()
{
    dijkstra();
    for(int i=0;i<=k;i++) ans=min(ans,dis[t][i]);
    printf("%d",ans);
}
 
int main()
{
//  freopen("input.txt","r",stdin);
    readdata();
    work();
    return 0;
}
/**************************************************************
    Problem: 1389
    User: mzg1812
    Language: C++
    Result: 正确
    Time:136 ms
    Memory:12564 kb
****************************************************************/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值