传送门:牛客
题目描述:
奶牛们正在找工作。农场主约翰知道后,鼓励奶牛们四处碰碰运气。而且他还加了一条要求:一头牛在一个城市
最多只能赚D(1≤D≤1000)美元,然后它必须到另一座城市工作。当然,它可以在别处工作一阵子后又回到原
来的城市再最多赚D美元。而且这样的往返次数没有限制。
城市间有P(1≤P≤150)条单向路径连接,共有C(2≤C≤220)座城市,编号从1到C。奶牛贝茜当前处在城市S
(1≤S≤C)。路径i从城市A_i到城市B_i(1≤A_i≤C,1≤B_i≤C),在路径上行走不用任何花费。
为了帮助贝茜,约翰让它使用他的私人飞机服务。这项服务有F条(1≤F≤350)单向航线,每条航线是从城市J_i
飞到另一座城市K_i(1≤J_i≤C,1≤K_i≤C),费用是T_i(1≤T_i≤50000)美元。如果贝茜手中没有现钱,可以
用以后赚的钱来付机票钱。
贝茜可以选择在任何时候,在任何城市退休。如果在工作时间上不做限制,贝茜总共可以赚多少钱呢?如果赚的
钱也不会出现限制,就输出-1。
输入:
100 3 5 2 1
1 5
2 3
1 4
5 2 150
2 5 120
输出:
250
一道求最长路的题目,需要好好掌握
关于最长路:如果边权全是正数或者全是负数,可以使用dijkstradijkstradijkstra解决,但是如果边权有正有负,那么dijkstradijkstradijkstra将无法解决,此时只能寻求于SpfaSpfaSpfa
主要思路:
一种是判断以当前点结尾的最长路的边是否大于n−1n-1n−1条,如果大于就是有环的存在
这种方法比较好理解,因为我们的点最多只有n个,那么对于我们的最长路来说,如果没有环的问题的话,我们的路径数最多肯定等于点数-1,也就是一条路直接过去(如果不理解,可以自己画一下)关于更新部分,也非常好理解,当前的点既然能被更新,那么就说明目前这种状态是最长路,那么显然边数应该由他之前的点的最长路边数递增过来
部分代码:
下面中的cnt记录的是边数for(int i=0;iint v=edge[u][i].v;if(dis[v]dis[v]=dis[u]+edge[u][i].w;cnt[v]=cnt[u]+1;if(cnt[v]>c-1) return -1;if(!vis[v]) {q.push(v);vis[v]=1;}}}
另外一种是判断当前的点的更新次数是否大于n-1
这一种方法也是网上使用的较多的方式,这种方式主要是利用了BFS的性质.因为是Spfa的本质还是BFS.所以当Spfa中每次扩展一个点的时候.注意这个扩展指的是入队而不是松弛,有很多博客写的是松弛部分,这是错误的,因为我们在一次BFS中可能有多个相等数量边优化该点的操作,但是此时我们的BFS扩展的边数是相同的.类似于下面的4点

我们发现此时以该点为终点的最长路的边肯定是大于等于该点入队的次数的(因为即使我们每一个BFS多扩展一条边都更新了该点,该点入队的次数也不会大于边数),那么假设我们当前的点更新次数都比n-1大了,那就意味着我们的边数大于了n-1,那肯定存在环.
部分代码:
if(dis[u]+wdis[v]=dis[u]+w;if(!vis[v]){if(++cnt[v]>=n)//注意就是这个位置的判断。一定要保证在判vis之后,即判入队次数;而不是在判vis之前,即判松弛次数!!!{printf("YES\n");return;}vis[v]=1;q.push(v);}
}
但是,第二种方法的空间复杂度显然比第一种要大,时间复杂度也比第一种要大,理由这里就不讲了,可以自行百度或者思考,所以强烈建议使用第一种方法,毕竟第一种方法既好用,又好理解,为什么不用呢??
下面是这道题的代码部分(使用第一种方法判环):
#include
#include
#include
#include
#include
#include