【BOI 2002】 双调路径

双调路径

题目传送门

前言

可能是我语文不太好,题目没读懂,感谢同桌帮助我理清题意


题意

对着图讲吧:

如图,横轴表示花费的金钱,纵轴表示花费的时间,点表示一条路径

  1. ((5,5))和点((30,3))是优秀点

  2. ((10,10))、点((15,5))和点((23,8))是无用点

来解释一下:

  1. ((5,5))明显比点((15,5))优秀,因为花费同样时间,花费金钱少的优秀

  2. ((10,10))和点((23,8))则肯定是无用点,因为它们花费的时间和金钱都多

  3. ((5,5))和点((30,3))都是优秀点,因为一个花费的金钱少,一个花费的时间少(相当于不同维度)

推广为一般性问题:

设我们当前的路径表示为((x,y)),下一条路径表示为((u,v))

  1. 如果 (u)(x) 小但 (v)(y) 大,那么两条路都暂为优秀

  2. 如果 (u)(x) 大但 (v)(y) 小,那么两条路都暂为优秀

  3. 如果 (u)(x) 小且 (v)(y) 小,那么((u,v))暂为优秀,而((x,y))一定为无用点


分析

题目要求求出从 (s)(e) 的最小路径条数

理解了题意,那么应该很容易就能想出双限制的最短路做法吧(别在意名字...)

什么意思?

普通最短路就是距离限制,我们通过 (dis[x]) 表示到达点 (x) 的最小距离

本题最短路则是限制距离(时间)和花费,那我们就加一维嘛!

—— (dis[x][y]) 表示到达点 (x) 花费 (y) 块钱的最小距离(时间)

最后枚举花费 (i) ,然后找到优秀点 (dis[e][i]),有多少优秀点答案就是多少

怎么判断是否是优秀点?

设一个变量 (ans) (=) (2005020600)(极大值就可以) ,如果当前的 (dis[e][i]) (<) (ans) ,则是优秀点( (ans) 表示的是时间)


代码

感觉上面讲的还是挺清楚了qwq,现在贴出代码:

PS:没加树状数组优化什么的,跑得确实有点慢,(1s+)

#include <bits/stdc++.h>
using namespace std;
queue<pair<int,int> > q;
int n,m,s,E,p,r,c,t,tot,sum,ans=2005020600,maxn;
int dis[101][30001],vis[101][3001],head[520010];

struct node {
	int to,ti,val,net;
} e[520010];

inline void add(int u,int v,int w,int tim) {
	e[++tot].ti=tim;
	e[tot].to=v;
	e[tot].val=w;
	e[tot].net=head[u];
	head[u]=tot;
}

inline void spfa(int s) {   //其实就是板子 
	for(register int i=1;i<=n;i++) {
		for(register int j=0;j<=maxn;j++) {
			dis[i][j]=2005020600;
		}
	}
	dis[s][0]=0;    //到达点s花费0的最短时间为0 
	vis[s][0]=1;
	q.push(make_pair(s,0));   //为了不开两个queue,我选择开pair 
	while(!q.empty()) {
		int x=q.front().first;
		int y=q.front().second;
		q.pop();
		vis[x][y]=0;
		for(register int i=head[x];i;i=e[i].net) {
			int v=e[i].to;
			if(dis[v][y+e[i].val]>dis[x][y]+e[i].ti) {
				dis[v][y+e[i].val]=dis[x][y]+e[i].ti;
				if(!vis[v][y+e[i].val]) {
					vis[v][y+e[i].val]=1;
					q.push(make_pair(v,y+e[i].val));  //注意第二个存的是花费还不是dis[v][y+e[i].val]! 
				}
			}
		}
	}
}

int main() {
	scanf("%d%d%d%d",&n,&m,&s,&E);
	for(register int i=1;i<=m;i++) {
		scanf("%d%d%d%d",&p,&r,&c,&t);
		add(p,r,c,t);
		add(r,p,c,t);
		maxn+=c;   //maxn表示最多的花费上限 
	}
	spfa(s);
	for(register int i=0;i<=maxn;i++) {  //枚举花费找答案 
		if(dis[E][i]<ans) {
			sum++;
			ans=dis[E][i];
		}
	}
	printf("%d",sum);
	return 0;
}

最后,如果这篇题解有任何问题或不懂的地方,欢迎下方留言区评论,我会及时回复、改正,谢谢大家啊orz


原文地址:https://www.cnblogs.com/Eleven-Qian-Shan/p/13372291.html