【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


posted @ 2020-07-24 15:41  Eleven谦  阅读(25)  评论(0编辑  收藏