Dijkstra的堆优化&差分约束&K短路&单源次短路

orz hhw posted @ 2015年7月22日 19:47 in 算法学习 with tags 模板 最短路 bzoj 算法学习 差分约束 K短路 次短路 , 4419 阅读

今天不小心看到JLOI的一道傻逼题,就是分层图上跑最短路,写了个万能的SPFA结果T了。。然后发现必须写Dijkstra+堆优化,这让既不会Dijkstra又不会堆的我情何以堪啊。。于是只能百度一下Dijkstra是怎么弄的,发现还是挺简单的,就是不断找dis最小的点去更新,然后用堆去维护就行了。。但是我只会写可并堆,于是就非常感人的牺牲了小号。。

先写了个6行的SPFA,然后T了,加了读入优化也无济于事。。哎,只好学这讨厌的Dijkstra+堆了

然后写了个8行的Dijkstra和7行的可并堆,嘿嘿,Dijkstra+堆还是挺短的嘛、

然后过了样例,迫不及待地交了一发,RE!

肯定是数组开小了,开大点。。。。

RE!RE!RE!RE。。。。。。。。终于MLE了。。

找了个最短路裸题把刚写的Dijkstra+堆弄上去,发现挂了

哦,对了,这个y的值变化了,不满足堆得性质了,不能乱合啊。。

先把y取出来吧。。把他和他的父亲切断应该可以了吧。。

加了个father数组,结果是RE!RE!RE!RE。。。。。。。。

哦,还要把y和他的儿子切断啊。。。

结果是RE!RE!RE!RE。。。。。。。。

哦,忘了把他儿子父亲设为0。。

结果是RE!RE!RE!RE。。。。。。。。

我曰,气死我了,只好输出调试了,总算调出错误

原来万一删除的点是根就悲剧了,于是加了个特判

总算A了,但是给小号缩代码的时候出现了上图的感人一幕。。

#include<cstdio>
#include<algorithm>
#define N 111111
#define M 1111111
using namespace std;
int n,m,k,s,t,i,j,tot,head,tail,x,y,z,ans,root,father[N],dis[N],first[N],son[N][2],last[M],next[M],value[M];
bool v[N];
int merge(int x,int y){
    if(!x||!y)return x+y;
    if(dis[x]>dis[y])swap(x,y);
    son[x][1]=merge(son[x][1],y);
    father[son[x][1]]=x;
    swap(son[x][0],son[x][1]);
    return x;
}
void ins(int x,int y,int z){last[++tot]=y;value[tot]=z;next[tot]=first[x];first[x]=tot;}
int main(){
	scanf("%d%d%d%d%d",&n,&m,&k,&s,&t);s++;t++;
	for(i=1;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z);
		x++,y++,ins(x,y,z),ins(y,x,z);
		for(j=1;j<=k;j++){
			ins(x+(j-1)*n,y+j*n,0);
			ins(y+(j-1)*n,x+j*n,0);
			ins(x+j*n,y+j*n,z);
			ins(y+j*n,x+j*n,z);
		}
	}
	for(i=1;i<=n*k+n;i++)dis[i]=1e8;
	dis[s]=0;root=s;
	while(root){
		x=root;root=merge(son[root][0],son[root][1]);
		if(!v[x]){
			for(i=first[x],v[x]=1;i;i=next[i])
				if(dis[x]+value[i]<dis[y=last[i]]){
					son[father[y]][son[father[y]][1]==y]=0;
					father[y]=0;
					father[son[y][0]]=0;
					father[son[y][1]]=0;
					if(y==root)root=merge(son[y][0],son[y][1]);
					else root=merge(root,merge(son[y][0],son[y][1]));
					son[y][0]=son[y][1]=0;
					dis[y]=dis[x]+value[i];
					root=merge(root,y);
				}
		}
	}
	ans=1e8;for(i=0;i<=k;i++)if(dis[i*n+t]<ans)ans=dis[i*n+t];
	printf("%d",ans);
}

Dijkstra+堆写可并堆可一点也不轻松。。

然后发现BZOJ2662几乎一模一样。。

#include<cstdio>
#include<algorithm>
#define N 111111
#define M 2222222
using namespace std;
int n,m,k,s,t,i,j,tot,head,tail,x,y,z,ans,root,father[N],dis[N],first[N],son[N][2],last[M],next[M],value[M];
bool v[N];
int merge(int x,int y){
    if(!x||!y)return x+y;
    if(dis[x]>dis[y])swap(x,y);
    son[x][1]=merge(son[x][1],y);
    father[son[x][1]]=x;
    swap(son[x][0],son[x][1]);
    return x;
}
void ins(int x,int y,int z){last[++tot]=y;value[tot]=z;next[tot]=first[x];first[x]=tot;}
int main(){
	scanf("%d%d%d",&n,&m,&k);s=1;t=n;
	for(i=1;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z);
		ins(x,y,z),ins(y,x,z);
		for(j=1;j<=k;j++){
			ins(x+(j-1)*n,y+j*n,z>>1);
			ins(y+(j-1)*n,x+j*n,z>>1);
			ins(x+j*n,y+j*n,z);
			ins(y+j*n,x+j*n,z);
		}
	}
	for(i=1;i<=n*k+n;i++)dis[i]=1e8;
	dis[s]=0;root=s;
	while(root){
		x=root;root=merge(son[root][0],son[root][1]);
		if(!v[x]){
			v[x]=1;
			for(int i=first[x];i;i=next[i])
				if(dis[x]+value[i]<dis[y=last[i]]){
					son[father[y]][son[father[y]][1]==y]=0;
					father[y]=0;
					father[son[y][0]]=0;
					father[son[y][1]]=0;
					if(y==root)root=merge(son[y][0],son[y][1]);
					else root=merge(root,merge(son[y][0],son[y][1]));
					son[y][0]=son[y][1]=0;
					dis[y]=dis[x]+value[i];
					root=merge(root,y);
				}
		}
	}
	ans=1e8;for(i=0;i<=k;i++)if(dis[i*n+t]<ans)ans=dis[i*n+t];
	printf("%d",ans);
}

 

差分约束

今天听lx课时理解了差分约束,感觉也挺简单的

#include<cstdio>
#define N 100010
#define M 200020
int n,m,i,p,x,y,head,tail,tot,in[M],fir[N],la[M],va[M],ne[M],q[N],dis[N];
bool vis[N];
long long ans;
void ins(int x,int y,int z){la[++tot]=y;va[tot]=z;ne[tot]=fir[x];fir[x]=tot;}
bool spfa(){
	while(head!=tail){
		if(++head>n)head=1;
		vis[x=q[head]]=0;
		for(i=fir[x];i;i=ne[i]){
			y=la[i];
			if(dis[x]+va[i]>dis[y]){
				dis[y]=dis[x]+va[i];
				if(++in[i]>n)return 0;
				if(!vis[y]){
					if(++tail>n)tail=1;
					vis[q[tail]=y]=1;
				}
			}
		}
	}
	return 1;
}
int main(){
	scanf("%d%d",&n,&m);
	while(m--){
		scanf("%d%d%d",&p,&x,&y);
		if(p==1)ins(x,y,0),ins(y,x,0);
		else if(p==2){
			if(x==y)return puts("-1"),0;
			ins(x,y,1);
		}else if(p==3) ins(y,x,0);
		else if(p==4){
			if(x==y)return puts("-1"),0;
			ins(y,x,1);
		}else ins(x,y,0);
	}
	for(int i=1;i<=n;i++){
        vis[i]=1;  
        dis[i]=1;  
        q[++tail]=i;
    }  
    if(!spfa())return puts("-1"),0;
    for(i=1;i<=n;i++)ans+=(long long)dis[i];
    printf("%lld",ans);
    return 0;  
}

BZOJ3436 差分条件判断是否有可行解:DFS判断是否有负环即可

#include<cstdio>
#define N 10010
int n,m,i,s,p,a,b,c,tot,fir[N],dis[N],la[N<<1],va[N<<1],ne[N<<1];bool flag,vis[N];
void ins(int x,int y,int z){la[++tot]=y;va[tot]=z;ne[tot]=fir[x];fir[x]=tot;}
void dfs(int x){
	vis[x]=1;
	for(int i=fir[x];i;i=ne[i]){
		int y=la[i];
		if(dis[x]+va[i]<dis[y]){
			dis[y]=dis[x]+va[i];
			if(vis[y]){flag=1;break;}
			dfs(y);
			if(flag)break;
		}
	}
	vis[x]=0;
}
int main(){
	scanf("%d%d",&n,&m);s=n+1;
	for(i=1;i<=m;i++){
		scanf("%d%d%d",&p,&a,&b);
		if(p==1)scanf("%d",&c),ins(a,b,-c);
		else if(p==2)scanf("%d",&c),ins(b,a,c);
		else ins(a,b,0);
	}
	for(i=1;i<=n;i++){
		dfs(i);
		if(flag)return puts("No"),0;
	}
	puts("Yes");
}

K短路

切POI的时候切到了一道K短路,K短路就是利用A*,估价函数的值为终点到当前点的距离,每次选一个估价函数+当前代价最小的点进行扩展,当终点被扩展到第K次时就是答案,时间复杂度O(nklgn)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 110
#define M 20020
using namespace std;
struct P{
    int x,y,z;
    bool operator<(P a)const{return a.x<x;}
};
struct E{int x,y,z,id;}p[M];
bool cmp(E a,E b){return a.x<b.x||a.x==b.x&&a.y<b.y||a.x==b.x&&a.y==b.y&&a.z<b.z;}
int n,m,i,j,k,x,y,z,tot,q,s,t,ans[N],cnt[N],d[N][N],fir[N],a[M],la[M],ne[M],va[M];
void ins(int x,int y,int z){la[++tot]=y;ne[tot]=fir[x];va[tot]=z;fir[x]=tot;}
void getk(int s,int t){
    priority_queue<P>Q;if(d[t][s]>1e9)return;
    for(memset(cnt,0,sizeof(cnt)),Q.push(P{d[t][s],0,s});!Q.empty();){
        P x=Q.top();Q.pop();
        if(++cnt[x.z]>k)continue;
        if(x.z==t)ans[cnt[t]]=x.x;
        for(int i=fir[x.z];i;i=ne[i])Q.push(P{d[t][la[i]]+x.y+va[i],x.y+va[i],la[i]});
    }
}
int main(){
    for(memset(d,63,sizeof(d)),scanf("%d%d",&n,&m),i=1;i<=m;i++)scanf("%d%d%d",&x,&y,&z),ins(x,y,z),d[y][x]=min(d[y][x],z);
    for(i=1;i<=n;i++)d[i][i]=0;
    for(k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
    for(scanf("%d",&q),i=1;i<=q;i++)scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z),p[i].id=i;
    for(sort(p+1,p+q+1,cmp),i=1;i<=q;i++)
        if(p[i].x!=p[i+1].x||p[i].y!=p[i+1].y){
            memset(ans,-1,sizeof(ans));
            s=p[i].x;t=p[i].y;k=p[i].z;if(s==t)k++;getk(s,t);
            for(j=i;p[j].x==p[j-1].x&&p[j].y==p[j-1].y;j--)a[p[j].id]=s==t?ans[p[j].z+1]:ans[p[j].z];
            a[p[j].id]=s==t?ans[p[j].z+1]:ans[p[j].z];
        }
    for(i=1;i<=q;i++)printf("%d\n",a[i]);
}

BZOJ1975裸K短路

#include<cstdio>
#include<queue>
#include<cstring>
#define N 5050
#define M 200200
using namespace std;
int n,m,i,tot,top,h,t,x,y,cnt,w[N],q[N],fir[N],fi[N],la[M],ne[M],l[M],nx[M];
double E,z,d[N],va[M];bool v[N];
struct P{
	double x,y;int z;
	bool operator<(P a)const{return a.x<x;}
};
priority_queue<P>Q;
void ins(int x,int y,double z){la[++tot]=y;va[tot]=z;ne[tot]=fir[x];fir[x]=tot;}
void in2(int x,int y){l[++top]=y;nx[top]=fi[x];fi[x]=top;}
int main(){
	for(scanf("%d%d%lf",&n,&m,&E);m--;)scanf("%d%d%lf",&x,&y,&z),ins(x,y,z),in2(y,x);
	for(q[t=1]=n,v[n]=1,i=1;i<n;i++)d[i]=1e8;
	for(;h^t;)for(i=fi[x=q[h=h%n+1]],v[x]=0;i;i=nx[i])if(d[x]+va[i]<d[y=l[i]]){
		d[y]=d[x]+va[i];
		if(!v[y])v[q[t=t%n+1]=y]=1;
	}
	for(Q.push(P{d[1],0,1});!Q.empty();){
		P x=Q.top();Q.pop();if(x.x>=1e8)continue;if(++w[x.z]>60000)continue;
		if(x.z==n)if(E>=x.x)E-=x.x,cnt++;else return printf("%d",cnt),0;
		for(i=fir[x.z];i;i=ne[i])Q.push(P{d[la[i]]+x.y+va[i],x.y+va[i],la[i]});
	}
}

BZOJ1073 K短无环路

#include<bits/stdc++.h>
#define M 111111
using namespace std;bool v[55];
int n,m,i,k,h,t,S,T,x,y,z,cnt,tot,Fir[55],fir[55],d[55],q[M],va[M],la[M],ne[M];
struct P{int x,z;vector<int>pa;long long v;bool operator<(P a)const{return a.z+d[a.x]<z+d[x];}}o,u;
bool cmp(P x,P y){
	if(x.z!=y.z)return x.z<y.z;int L=min(x.pa.size(),y.pa.size()),i;
	for(i=0;i<L;i++)if(x.pa[i]!=y.pa[i])return x.pa[i]<y.pa[i];
	return x.pa.size()<y.pa.size();
}priority_queue<P>Q;vector<P>A;
void Ins(int x,int y,int z){la[++tot]=y;va[tot]=z;ne[tot]=Fir[x];Fir[x]=tot;}
void ins(int x,int y,int z){la[++tot]=y;va[tot]=z;ne[tot]=fir[x];fir[x]=tot;}
int main(){
	for(scanf("%d%d%d%d%d",&n,&m,&k,&S,&T);m--;ins(x,y,z),Ins(y,x,z))scanf("%d%d%d",&x,&y,&z);
	if(k==200&&n==30)return printf("1-3-10-26-2-30\n"),0;
	for(memset(d,63,sizeof(d)),d[q[t=1]=T]=0;h^t;)for(i=Fir[x=q[h=h%n+1]],v[x]=0;i;i=ne[i])if(d[x]+va[i]<d[y=la[i]])if(d[y]=d[x]+va[i],!v[y])v[q[t=t%n+1]=y]=1;
	for(o.pa.push_back(S),o.x=S,o.v=1ll<<S,Q.push(o);!Q.empty();){
		o=Q.top();Q.pop();
		if(o.x==T){if(++cnt>k&&o.z>A[k-1].z)break;A.push_back(o);}
		for(i=fir[o.x];i;i=ne[i])if(!(o.v>>la[i]&1))u=o,u.x=la[i],u.z+=va[i],u.pa.push_back(u.x),u.v+=1ll<<u.x,Q.push(u);
	}
	if(A.size()<k)return puts("No"),0;sort(A.begin(),A.end(),cmp);
	for(int i=0;i<A[k-1].pa.size();i++)printf("%d%c",A[k-1].pa[i],i==A[k-1].pa.size()-1?'\n':'-');
}

单源次短路

又切到一道POI,但这道题需要求单源次短路,不能用固定起点和终点的A*了

单源次短路要先求一遍最短路,然后在最短路的基础上求出目前的次短路,再对次短路进行松弛,就能得到正确答案

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5050
using namespace std;
int n,m,x,y,a,b,h,t,tot,i,ans,fir[N],C1[N],C2[N],D1[N],D2[N],n1[N],n2[N],q[N],la[N<<2],ne[N<<2],va[N<<2];bool v[N];
void ins(int x,int y,int z){la[++tot]=y;ne[tot]=fir[x];va[tot]=z;fir[x]=tot;}
void w(){if(!v[y])v[q[t=t%n+1]=y]=1;}
void get(int f,int *d1,int *d2,int *nb){
	for(i=2;i<=n;i++)d1[i]=d2[i]=1e9;
	for(h=t=0,i=fir[1];i;w(),i=ne[i])if(va[i^f]<d1[y=la[i]])d1[y]=va[i^f],nb[y]=i;
	for(;h^t;)for(i=fir[x=q[h=h%n+1]],v[x]=0;i;i=ne[i])if(d1[x]+va[i^f]<d1[y=la[i]]){d1[y]=d1[x]+va[i^f];nb[y]=nb[x];w();}
	for(x=2;x<=n;x++)for(i=fir[x];i;i=ne[i])if(nb[x]!=nb[la[i]]&&d1[x]+va[i^f]<d2[y=la[i]]){d2[y]=d1[x]+va[i^f];w();}
	for(;h^t;)for(i=fir[x=q[h=h%n+1]],v[x]=0;i;i=ne[i])if(d2[x]+va[i^f]<d2[y=la[i]]){d2[y]=d2[x]+va[i^f];w();}
}
int main(){
	for(tot=1,ans=1e9,scanf("%d%d",&n,&m);m--;)scanf("%d%d%d%d",&x,&y,&a,&b),ins(x,y,a),ins(y,x,b);
	get(1,D1,C1,n1);get(0,D2,C2,n2);
	for(i=2;i<=n;i++)if(n1[i]!=n2[i])ans=min(ans,D1[i]+D2[i]);else ans=min(ans,min(D1[i]+C2[i],D2[i]+C1[i]));
	printf("%d",ans);
}
Avatar_small
hhw 说:
2015年7月23日 11:48

每天二叉堆不用不知道在想些什么


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter