
分层图上的最短路。
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;
}
进入第一版~
其实没必要用线段树,因为是求前缀的最小值,直接树状数组就可以了。。
【BZOJ 1375】 [Baltic2002]Bicriterial routing 双调路径
原文地址:http://blog.csdn.net/regina8023/article/details/43851167