【洛谷 P1137】旅行计划——spfa拓展

来源:互联网 发布:淘宝清退品牌 编辑:程序博客网 时间:2024/06/10 01:43

题目描述

小明要去一个国家旅游。这个国家有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的最优贸易,那道题有环,可是没关系!转几圈不就是了。


原创粉丝点击