【洛谷 P1137】旅行计划——spfa拓展
2017-08-21 10:58
211 查看
题目描述
小明要去一个国家旅游。这个国家有N个城市,编号为1~N,并且有M条道路连接着,小明准备从其中一个城市出发,并只往东走到城市i停止。
所以他就需要选择最先到达的城市,并制定一条路线以城市i为终点,使得线路上除了第一个城市,每个城市都在路线前一个城市东面,并且满足这个前提下还希望游览的城市尽量多。
现在,你只知道每一条道路所连接的两个城市的相对位置关系,但并不知道所有城市具体的位置。现在对于所有的i,都需要你为小明制定一条路线,并求出以城市i为终点最多能够游览多少个城市。
输入格式:
输入的第1行为两个正整数N, M。
接下来M行,每行两个正整数x, y,表示了有一条连接城市x与城市y的道路,保证了城市x在城市y西面。
输出格式:
输出包括N行,第i行包含一个正整数,表示以第i个城市为终点最多能游览多少个城市。
数据范围:
对于100%的数据,N ≤ 100000,M ≤ 200000
这道题第一反应dp,一个点的答案就是所有指向它的点中最大的+1,。可是正向dp不是很方便,因为初始点不止一个。
然后我就接触了一个黑科技——记忆化搜索。
本质还是搜索,不过是带动归思想的搜索,剪掉的枝多到不知道那里去了…
搜索过程中先判断该状态有没有记录,如果有就直接返回,如果没有就往下搜索并记录。
效率和dp一样?
代码如下;
另外说一下spfa的方法。
spfa大概是最常用的最短路算法了,重要的不是它的代码,而是它的思想,很多不是求最短路的里的题也可以用“hortest path faster algorithm”/滑稽
spfa是怎么实现的?
每次取出队首元素u,尝试对其指向的点v进行松弛操作,如果更新成功了,并且v不在队列中,就把v入队,不断重复直到队列为空。
好像有点像bfs…
bfs是针对无权图求最短路的好方法,spfa却可以用于有权图。
这两个的作用远不止最短路…
只要可以用前一个点更新后一个点的题,好像它们都能得分…
这个题显然就是这样的辣!(我觉得和dp没什么区别)
因为是无权图,所以可以用也bfs(其实原理一样)。
如果已知前一个点的f[u],那么就能推出后一个点f[v]=f[u]+1;
所以我们先找出初始点入队就可以了。
可是需要稍微改动一下。
因为一个点可能通过多个点到达,而这几个点的数值可能不一样。怎么办?
很显然,我们可以只用最大的点来更新。
由于bfs是通过队列实现的,所以点是按照数值从小到大的顺序处理的,所以越大的点处理地越晚。
那我们就可以只用能到达这个点中最晚被处理的点更新它就好啦。这样保证每个点被处理一次。
用spfa解非最短路问题还有noip 2009的最优贸易,那道题有环,可是没关系!转几圈不就是了。
小明要去一个国家旅游。这个国家有N个城市,编号为1~N,并且有M条道路连接着,小明准备从其中一个城市出发,并只往东走到城市i停止。
所以他就需要选择最先到达的城市,并制定一条路线以城市i为终点,使得线路上除了第一个城市,每个城市都在路线前一个城市东面,并且满足这个前提下还希望游览的城市尽量多。
现在,你只知道每一条道路所连接的两个城市的相对位置关系,但并不知道所有城市具体的位置。现在对于所有的i,都需要你为小明制定一条路线,并求出以城市i为终点最多能够游览多少个城市。
输入格式:
输入的第1行为两个正整数N, M。
接下来M行,每行两个正整数x, y,表示了有一条连接城市x与城市y的道路,保证了城市x在城市y西面。
输出格式:
输出包括N行,第i行包含一个正整数,表示以第i个城市为终点最多能游览多少个城市。
数据范围:
对于100%的数据,N ≤ 100000,M ≤ 200000
这道题第一反应dp,一个点的答案就是所有指向它的点中最大的+1,。可是正向dp不是很方便,因为初始点不止一个。
然后我就接触了一个黑科技——记忆化搜索。
本质还是搜索,不过是带动归思想的搜索,剪掉的枝多到不知道那里去了…
搜索过程中先判断该状态有没有记录,如果有就直接返回,如果没有就往下搜索并记录。
效率和dp一样?
代码如下;
#include<cstdio> #include<iostream> using namespace std; struct edge{ int to,next; }ed[400001]; int head[100001]={0}; int n,m,size=0; int dp[100001]={0}; void read(int &x) { char c=getchar();x=0; while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); } void add(int from,int to) { size++; ed[size].to=to; ed[size].next=head[from]; head[from]=size; } int dfs(int u) { if(dp[u]) return dp[u]; dp[u]=1; for(int i=head[u];i;i=ed[i].next) { int v=ed[i].to; dp[u]=max(dp[u],dfs(v)+1); } return dp[u]; } int main() { read(n),read(m); for(int i=1;i<=m;i++) { int u,v; read(u),read(v); add(v,u); } for(int i=1;i<=n;i++) { printf("%d\n",dfs(i)); } return 0; }
另外说一下spfa的方法。
spfa大概是最常用的最短路算法了,重要的不是它的代码,而是它的思想,很多不是求最短路的里的题也可以用“hortest path faster algorithm”/滑稽
spfa是怎么实现的?
每次取出队首元素u,尝试对其指向的点v进行松弛操作,如果更新成功了,并且v不在队列中,就把v入队,不断重复直到队列为空。
好像有点像bfs…
bfs是针对无权图求最短路的好方法,spfa却可以用于有权图。
这两个的作用远不止最短路…
只要可以用前一个点更新后一个点的题,好像它们都能得分…
这个题显然就是这样的辣!(我觉得和dp没什么区别)
因为是无权图,所以可以用也bfs(其实原理一样)。
如果已知前一个点的f[u],那么就能推出后一个点f[v]=f[u]+1;
所以我们先找出初始点入队就可以了。
可是需要稍微改动一下。
因为一个点可能通过多个点到达,而这几个点的数值可能不一样。怎么办?
很显然,我们可以只用最大的点来更新。
由于bfs是通过队列实现的,所以点是按照数值从小到大的顺序处理的,所以越大的点处理地越晚。
那我们就可以只用能到达这个点中最晚被处理的点更新它就好啦。这样保证每个点被处理一次。
#include<cstdio> #include<iostream> #include<queue> using namespace std; struct edge{ int next,to; }ed[400001]; int head[100001]={0}; int f[100001]={0}; bool vis[100001]={0}; int pre[100001]={0}; int n,m,size=0; void read(int &x) { char c=getchar();x=0; while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); } void add(int from,int to) { size++; ed[size].to=to; ed[size].next=head[from]; head[from]=size; } void spfa() { queue <int> q; for(int i=1;i<=n;i++) { if(!pre[i]) { q.push(i); vis[i]=1; f[i]=1; } } while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i;i=ed[i].next) { int v=ed[i].to; f[v]=f[u]+1; pre[v]--; if(!vis[v]&&!pre[v]) //如果再没有点能到v,说明u是最后一个能更新v的点 { q.push(v); vis[v]=1; } } } } int main() { read(n),read(m); for(int i=1;i<=m;i++) { int x,y; read(x),read(y); add(x,y); pre[y]++; //记录能到达y点的点的数量 } spfa(); for(int i=1;i<=n;i++) { printf("%d\n",f[i]); } return 0; }
用spfa解非最短路问题还有noip 2009的最优贸易,那道题有环,可是没关系!转几圈不就是了。