【BZOJ 1375】 [Baltic2002]Bicriterial routing 双调路径

1375: [Baltic2002]Bicriterial routing 双调路径

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 299   Solved: 115
[ Submit][ Status]

Description

来越多,因此选择最佳路径是很现实的问题。城市的道路是双向的,每条道路有固定的旅行时间以及需要支付的费用。路径由连续的道路组成。总时间是各条道路旅行时间的和,总费用是各条道路所支付费用的总和。同样的出发地和目的地,如果路径A比路径B所需时间少且费用低,那么我们说路径A比路径B好。对于某条路径,如果没有其他路径比它好,那么该路径被称为最优双调路径。这样的路径可能不止一条,或者说根本不存在。 给出城市交通网的描述信息,起始点和终点城市,求最优双条路径的条数。城市不超过100个,边数不超过300,每条边上的费用和时间都不超过100。

Input

第一行给出有多少个点,多少条边,开始点及结束点. 下面的数据用于描述这个地图

Output

有多少条最优双调路径

Sample Input

4 5 1 4
2 1 2 1
3 4 3 1
2 3 1 2
3 1 1 4
2 4 2 4

Sample Output

2

HINT

【BZOJ 1375】 [Baltic2002]Bicriterial routing 双调路径_第1张图片


分层图上的最短路。


f[i][j]表示走到i结点,费用为j的最少时间,直接跑spfa即可。


#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <queue>
#define inf 0x3f3f3f3f
#define maxc (n-1)*100
using namespace std;
struct edge
{
	int y,ne,c,t;
}e[1000];
struct now
{
	int p,c;
};
int tot=0,n,m;
struct data
{
	int t,f;
}d[105][10005];
int s,t,h[105],inq[105][10005];
void Addedge(int x,int y,int co,int ti)
{
	tot++;
	e[tot].y=y;
	e[tot].ne=h[x];
	h[x]=tot;
	e[tot].c=co;
	e[tot].t=ti;
}
void spfa()
{
	for (int i=1;i<=n;i++)
		for (int j=0;j<=maxc;j++)
			d[i][j].f=0,inq[i][j]=0,d[i][j].t=inf;
	queue<now> q;
	now x;
	x.p=s,x.c=0;
	d[s][0].f=1,d[s][0].t=0;
	inq[s][0]=1;
	q.push(x);
	while (!q.empty())
	{
		x=q.front();
		q.pop();
		inq[x.p][x.c]=0;
                for (int i=h[x.p];i;i=e[i].ne)
		{
			int y=e[i].y;
			int co=e[i].c+x.c;
			if (co>maxc) continue;
			if (d[y][co].t>d[x.p][x.c].t+e[i].t)
			{
				d[y][co].t=d[x.p][x.c].t+e[i].t;
				d[y][co].f=1;
				if (!inq[y][co])
				{
					now aa;
					aa.p=y,aa.c=co;
					q.push(aa),inq[y][co]=1;
				}
			}
		}
	}
}
int main()
{
        scanf("%d%d%d%d",&n,&m,&s,&t);
	for (int i=1;i<=m;i++)
	{
		int x,y,ti,co;
		scanf("%d%d%d%d",&x,&y,&co,&ti);
		Addedge(x,y,co,ti);
		Addedge(y,x,co,ti);
	}
	spfa();
	int ans=0,minn=maxc+10;
        for (int i=0;i<=maxc;i++)
	{
		if (!d[t][i].f) continue;
		if (d[t][i].t>=minn) continue;
		minn=d[t][i].t;
		ans++;
	}
	cout<<ans<<endl;
	return 0;
}


wa是因为求答案时写了break,应该是continue。


其实这个题还可以进行优化:

如果存在f[i][k],满足k<j且f[i][k]<f[i][j],那么f[i][j]一定不是最优解。


因此我们可以给每一个结点建一棵线段树,维护最小值即可。


#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <queue>
#define inf 0x3f3f3f3f
#define maxc (n-1)*100+5
using namespace std;
struct edge
{
	int y,ne,c,t;
}e[1000];
struct now
{
	int p,c;
};
int tot=0,n,m;
struct data
{
	int t,f;
}d[105][10005];
int s,t,h[105],inq[105][10005];
struct segtree
{
	int l,r,minn;
}T[105][40005];
void Addedge(int x,int y,int co,int ti)
{
	tot++;
	e[tot].y=y;
	e[tot].ne=h[x];
	h[x]=tot;
	e[tot].c=co;
	e[tot].t=ti;
}
void Build(int now,int x,int l,int r)
{
	T[now][x].l=l,T[now][x].r=r;
	if (l==r) 
	{
		T[now][x].minn=maxc;
		return;
	}
	int m=(l+r)>>1;
	Build(now,x<<1,l,m);
	Build(now,(x<<1)+1,m+1,r);
	T[now][x].minn=maxc;
}
void Update(int now,int x,int p,int k)
{
	if (T[now][x].l==T[now][x].r&&T[now][x].l==p)
	{
		T[now][x].minn=min(k,T[now][x].minn);
		return;
	}
	int m=(T[now][x].l+T[now][x].r)>>1;
	if (p<=m) Update(now,x<<1,p,k);
	else Update(now,(x<<1)+1,p,k);
	T[now][x].minn=min(T[now][x<<1].minn,T[now][(x<<1)+1].minn);
}
int Get(int now,int x,int l,int r)
{
	if (T[now][x].l>=l&&T[now][x].r<=r)
		return T[now][x].minn;
	int m=(T[now][x].l+T[now][x].r)>>1;
	if (r<=m) return Get(now,x<<1,l,r);
	if (l>m+1) return Get(now,(x<<1)+1,l,r);
	return min(Get(now,x<<1,l,r),Get(now,(x<<1)+1,l,r));
}
void spfa()
{
	for (int i=1;i<=n;i++)
		for (int j=0;j<=maxc;j++)
			d[i][j].f=0,inq[i][j]=0,d[i][j].t=inf;
	queue<now> q;
	now x;
	x.p=s,x.c=0;
	d[s][0].f=1,d[s][0].t=0;
	inq[s][0]=1;
	Update(s,1,0,0);
	q.push(x);
	while (!q.empty())
	{
		x=q.front();
		q.pop();
		inq[x.p][x.c]=0;
        for (int i=h[x.p];i;i=e[i].ne)
		{
			int y=e[i].y;
			int co=e[i].c+x.c;
			if (co>maxc) continue;
			if (d[y][co].t>d[x.p][x.c].t+e[i].t&&Get(y,1,0,co)>d[x.p][x.c].t+e[i].t)
			{
				d[y][co].t=d[x.p][x.c].t+e[i].t;
				d[y][co].f=1;
				Update(y,1,co,d[y][co].t);
				if (!inq[y][co])
				{
					now aa;
					aa.p=y,aa.c=co;
					q.push(aa),inq[y][co]=1;
				}
			}
		}
	}
}
int main()
{
        scanf("%d%d%d%d",&n,&m,&s,&t);
	for (int i=1;i<=m;i++)
	{
		int x,y,ti,co;
		scanf("%d%d%d%d",&x,&y,&co,&ti);
		Addedge(x,y,co,ti);
		Addedge(y,x,co,ti);
	}
	for (int i=1;i<=n;i++)
		Build(i,1,0,maxc);
	spfa();
	int ans=0,minn=maxc+10;
        for (int i=0;i<=maxc;i++)
	{
		if (!d[t][i].f) continue;
		if (d[t][i].t>=minn) continue;
		minn=d[t][i].t;
		ans++;
	}
	cout<<ans<<endl;
	return 0;
}



进入第一版~


其实没必要用线段树,因为是求前缀的最小值,直接树状数组就可以了。。

你可能感兴趣的:(线段树,SPFA,OI,bzoj)