下面给出的主要是灵活运用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
****************************************************************/