y总讲解
重点是 源点满足条件,建立的边的方向,求得是最短路还是最长路
蓝书讲解
总结:
1. 题目中要求求出最大值,则要将所有的不等式变为形如 ,求单源最短路。(1)若存在负环,则无解。 (2)若无法到达,则有无数种解。 (3)不满足上述两条,则有解。
2. 题目中要求求出最小值,则要将所有的不等式变为形如 , 求单源最长路。(1)若存在正环,则无解。 (2)若无法到达,则有无数种解。 (3)不满足上述两条,则有解。
3. 若不等式形如 ,则将其变为形如
。
4. 若不等式形如 ,则将其变为形如
。
5. 建图规则: 不等式关系
,则让
向
连一条有向边,边权为
。
小技巧:
1. 若spfa判正/负环出现超时,则可以尝试使用将 队列 改为 栈 来存储
2. 若spfa判正/负环出现超时,则可以认为如果经过的点的数量 很大时,如
,则认为它有正/负环
3. 求最小值,则求最长路,大小关系全变成 ;求最大值,则求最短路,大小关系全变成
。 连边时为
向
连一条长度为
的边。
例题:
1. AcWing 1169. 糖果
求分发的最少糖果,所以求单源最长路
这道题用队列的过不去,将队列改为栈,当spfa使用队列超时时再用这种优化,否则有时使用栈会超级慢
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=3e5+10;
#define ll long long
struct node{
int nex,to,w;
}e[M];
int head[N],cnt,tot[N],vis[N];
int n,m,q[N];
ll d[N];
void add(int u,int v,int w){
e[++cnt].nex=head[u];
e[cnt].to=v;
e[cnt].w=w;
head[u]=cnt;
}
bool spfa(){
int hh=0,tt=1;
memset(d,-0x3f,sizeof(d));
d[0]=0; q[0]=0; vis[0]=1;
while(hh!=tt){
int u=q[--tt];
vis[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].to,w=e[i].w;
if(d[v]<d[u]+w){
d[v]=d[u]+w;
tot[v]=tot[u]+1;
if(tot[v]>=n+1) return false;
if(!vis[v]){
q[tt++]=v;
vis[v]=1;
}
}
}
}
return true;
}
int main(){
scanf("%d%d",&n,&m);
while(m--){
int x,a,b;
scanf("%d%d%d",&x,&a,&b);
if(x==1) add(b,a,0),add(a,b,0);
else if(x==2) add(a,b,1);
else if(x==3) add(b,a,0);
else if(x==4) add(b,a,1);
else add(a,b,0);
}
for(int i=1;i<=n;i++) add(0,i,1);
if(!spfa()) printf("-1\n");
else{
ll ans=0;
for(int i=1;i<=n;i++) ans+=d[i];
printf("%lld\n",ans);
}
return 0;
}
2. AcWing 362. 区间
先将所有数向右平移一位,保证 不会变为
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10,M=N*3+10;
struct node{
int nex,to,w;
}e[M];
int head[N],cnt,vis[N],q[N];
int d[N];
int n;
void add(int u,int v,int w){
e[++cnt].nex=head[u];
e[cnt].to=v;
e[cnt].w=w;
head[u]=cnt;
}
void spfa(){
memset(d,-0x3f,sizeof(d));
int hh=0,tt=1;
q[hh]=0; vis[0]=1; d[0]=0;
while(hh!=tt){
int u=q[hh++];
if(hh==N) hh=0;
vis[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].to,w=e[i].w;
if(d[v]<d[u]+w){
d[v]=d[u]+w;
if(!vis[v]){
q[tt++]=v;
if(tt==N) tt=0;
vis[v]=1;
}
}
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=50001;i++){
add(i-1,i,0);
add(i,i-1,-1);
}
for(int i=1;i<=n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
a++; b++;
add(a-1,b,c);
}
spfa();
printf("%d\n",d[50001]);
return 0;
}
3. AcWing 1170. 排队布局
这道题先考虑 源点 是否可以到达每一个点,我们发现
向
连一条边,我们发现如果所有奶牛在数轴正半轴,则源点
无法到达所有点。由于题意说奶牛在数轴上任意位置,只是相对位置不变,因此我们把所有奶牛放在负半轴,这样 源点
就可以到达他们所有奶牛了。
对于该题求最大距离,则我们求最短路
对于第一小问,是否存在满足条件的方案,我们则把 个点全加入到队列里,省去了从源点
向他们每一个点连一条权值为
的边的操作。之后我们判断是否存在负环。若存在,则没有满足要求的方案;反之,则有方案
对于第二小问,我们则将所有奶牛全部向右平移,将第一个奶牛放在 位置为 的地方,那么
则是
号点与
号点之间的距离。若
,则说明二者之间距离任意大,输出
对于第三小问,则直接输出
#include<bits/stdc++.h>
using namespace std;
const int N=1010,M=2e4+N+10,INF=0x3f3f3f3f;
struct node{
int nex,to,w;
}e[M];
int head[N],cnt,tot[N],vis[N],q[N],d[N];
int n,m1,m2;
void add(int u,int v,int w){
e[++cnt].nex=head[u];
e[cnt].to=v;
e[cnt].w=w;
head[u]=cnt;
}
bool spfa(int siz){
memset(d,0x3f,sizeof(d));
memset(tot,0,sizeof(tot));
memset(vis,0,sizeof(vis));
int hh=0,tt=0;
for(int i=1;i<=siz;i++){
q[tt++]=i;
d[i]=0;
vis[i]=1;
}
while(hh!=tt){
int u=q[hh++];
if(hh==N) hh=0;
vis[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].to,w=e[i].w;
if(d[v]>d[u]+w){
d[v]=d[u]+w;
tot[v]=tot[u]+1;
if(tot[v]>=n) return false;
if(!vis[v]){
q[tt++]=v;
if(tt==N) tt=0;
vis[v]=1;
}
}
}
}
return true;
}
int main(){
scanf("%d%d%d",&n,&m1,&m2);
for(int i=1;i<n;i++) add(i+1,i,0);
while(m1--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if(b<a) swap(a,b);
add(a,b,c);
}
while(m2--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if(b<a) swap(a,b);
add(b,a,-c);
}
if(!spfa(n)) printf("-1\n");
else{
spfa(1);
if(d[n]==INF) printf("-2\n");
else printf("%d\n",d[n]);
}
return 0;
}
4. AcWing 393. 雇佣收银员![](https://i-blog.csdnimg.cn/blog_migrate/0fde75735348ebe125cb30f49834e970.png)
代表区间
共
个时间区间,我们将其共同向右平移一个小时,变成
,这样可以处理
变成
的情况。
我们设 表示当前时间
有几个人来申请工作,从时间
开始工作;设
表示时间
实际上岗人数;设
为
的前缀和
#include<bits/stdc++.h>
using namespace std;
const int N=30,M=100;
struct node{
int nex,to,w;
}e[M];
int head[N],tot[N],cnt;
int r[N],num[N],n;
int d[N],q[N],vis[N];
void add(int u,int v,int w){
e[++cnt].nex=head[u];
e[cnt].to=v;
e[cnt].w=w;
head[u]=cnt;
}
void build(int c){
memset(head,0,sizeof(head));
cnt=0;
for(int i=1;i<=24;i++){
add(i-1,i,0);
add(i,i-1,-num[i]);
}
for(int i=8;i<=24;i++) add(i-8,i,r[i]);
for(int i=1;i<=7;i++) add(i+16,i,r[i]-c);
add(0,24,c); add(24,0,-c);
}
bool spfa(int c){
build(c);
memset(d,-0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
memset(tot,0,sizeof(tot));
int hh=0,tt=1;
q[0]=0; vis[0]=1; d[0]=0;
while(hh!=tt){
int u=q[hh++];
if(hh==N) hh=0;
vis[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].to,w=e[i].w;
if(d[v]<d[u]+w){
d[v]=d[u]+w;
tot[v]=tot[u]+1;
if(tot[v]>=25) return false;
if(!vis[v]){
q[tt++]=v;
if(tt==N) tt=0;
vis[v]=1;
}
}
}
}
return true;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
for(int i=1;i<=24;i++)
scanf("%d",&r[i]);
memset(num,0,sizeof(num));
scanf("%d",&n);
for(int i=1;i<=n;i++){
int t;
scanf("%d",&t);
num[t+1]++;
}
bool success=false;
for(int i=0;i<=1000;i++){
if(spfa(i)){
printf("%d\n",i);
success=true;
break;
}
}
if(success==false) printf("No Solution\n");
}
return 0;
}