• 单源最短路的综合应用


    拓扑排序+迪杰斯特拉:https://www.acwing.com/problem/content/344/

    首先我们可以观察到:

    1.整个图的形状是一个个通过道路相连的团,而团与团之间通过航线相连。

    2.注意到团的非负性,可以用迪杰斯特拉,考虑到航线的单向性,我们可以考虑拓扑排序。

    具体的,我们把他们融合在一起:

    1.先输入道路扫出连通块。

    2.再输入航线确定入度

    3.按照拓扑序处理每一个连通块,当其中一个连通块入度为0时取出最小的放入队列中。

    AC代码:

    1. #include
    2. using namespace std;
    3. #define x first
    4. #define y second
    5. int t,r,p,s;
    6. int id[250100];
    7. int inf=0x3f3f3f3f;
    8. struct node{
    9. int dian,w;
    10. };
    11. vector<int> block[250100];
    12. vector edge[250100];
    13. int cnt=0;
    14. int deg[250100];
    15. int dis[250100];
    16. int st[250100];
    17. void dfs(int x,int c){
    18. id[x]=c;
    19. block[c].push_back(x);
    20. for(int i=0;isize();i++){
    21. int ck=edge[x][i].dian;
    22. if(id[ck]) continue;
    23. dfs(ck,c);
    24. }
    25. }
    26. void dij(){
    27. priority_queueint,int>,vectorint,int> >,greaterint,int> > > q;//(距离,编号)
    28. memset(dis,0x3f,sizeof(dis));
    29. dis[s]=0;
    30. for(int i=1;i<=cnt;i++){
    31. if(deg[i]) continue;
    32. for(int j=0;jsize();j++){
    33. int x=block[i][j];
    34. q.push({dis[x],x});
    35. }
    36. }
    37. while(!q.empty()){
    38. auto ck=q.top();
    39. q.pop();
    40. if(st[ck.y]) continue;
    41. st[ck.y]=1;
    42. for(int i=0;isize();i++){
    43. auto fk=edge[ck.y][i];
    44. if(dis[fk.dian]>dis[ck.y]+fk.w){
    45. dis[fk.dian]=dis[ck.y]+fk.w;
    46. if(id[fk.dian]==id[ck.y]) q.push({dis[fk.dian],fk.dian});
    47. }
    48. }
    49. for(int i=0;isize();i++){
    50. auto fk=edge[ck.y][i];
    51. if(id[fk.dian]!=id[ck.y]){
    52. deg[id[fk.dian]]--;
    53. if(deg[id[fk.dian]]==0){
    54. for(int j=0;jsize();j++){
    55. int x=block[id[fk.dian]][j];
    56. q.push({dis[x],x});
    57. }
    58. }
    59. }
    60. }
    61. }
    62. }
    63. int main(){
    64. cin>>t>>r>>p>>s;
    65. for(int i=1;i<=r;i++){
    66. int a,b,c;
    67. cin>>a>>b>>c;
    68. edge[a].push_back({b,c});
    69. edge[b].push_back({a,c});
    70. }
    71. //找连通块
    72. for(int i=1;i<=t;i++){
    73. if(id[i]==0) dfs(i,++cnt);
    74. }
    75. //加入航线
    76. for(int i=1;i<=p;i++){
    77. int a,b,c;
    78. cin>>a>>b>>c;
    79. edge[a].push_back({b,c});
    80. deg[id[b]]++;
    81. }
    82. //dij
    83. dij();
    84. for(int i=1;i<=t;i++){
    85. if(dis[i]>=inf/2) cout<<"NO PATH"<
    86. else cout<
    87. }
    88. }

    二分+单源最短路:

    首先二分出来一个x,我们想知道一条路上至少有几个是大于X的。我们每一次可以把边权大于X的看成1,比他小的看成0,于是问题就转换成了求单源点的最短路。

    AC代码:

    1. #include
    2. using namespace std;
    3. const int N = 1010, M = 20010;
    4. int n, m, k;
    5. int h[N], e[M], w[M], ne[M], idx;
    6. int dist[N];
    7. deque<int> q;
    8. bool st[N];
    9. void add(int a, int b, int c)
    10. {
    11. e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
    12. }
    13. bool check(int bound)
    14. {
    15. memset(dist, 0x3f, sizeof dist);
    16. memset(st, 0, sizeof st);
    17. q.push_back(1);
    18. dist[1] = 0;
    19. while (q.size())
    20. {
    21. int t = q.front();
    22. q.pop_front();
    23. if (st[t]) continue;
    24. st[t] = true;
    25. for (int i = h[t]; ~i; i = ne[i])
    26. {
    27. int j = e[i], x = w[i] > bound;
    28. if (dist[j] > dist[t] + x)
    29. {
    30. dist[j] = dist[t] + x;
    31. if (!x) q.push_front(j);
    32. else q.push_back(j);
    33. }
    34. }
    35. }
    36. return dist[n] <= k;
    37. }
    38. int main()
    39. {
    40. cin >> n >> m >> k;
    41. memset(h, -1, sizeof h);
    42. while (m -- )
    43. {
    44. int a, b, c;
    45. cin >> a >> b >> c;
    46. add(a, b, c), add(b, a, c);
    47. }
    48. int l = 0, r = 1e6 + 1;
    49. while (l < r)
    50. {
    51. int mid = l + r >> 1;
    52. if (check(mid)) r = mid;
    53. else l = mid + 1;
    54. }
    55. if (r == 1e6 + 1) cout << -1 << endl;
    56. else cout << r << endl;
    57. }

    DP+最短路:https://www.acwing.com/problem/content/343/

    可以观察到:

    从源点1到终点n一定存在一个分界点i,在1\rightarrow i上取到最小值,在i\rightarrow n上取到最大值。

    于是我们记maxx[i]表示i\rightarrow n上的最大值,minn[i]表示1\rightarrow i上的最小值,然后我们枚举每一个i即可。

    现在我们主要求这个数组,假如图是严格的拓扑序,那么DP转移就十分明显,但是现在的图可能存在回路,直接DP会陷入死循环,于是我们考虑最短路的思想。

    我们把距离的概念抽象成上面的两个数组的值,松弛操作也就是maxx[i]=max(w[i],maxx[x])

    注意虽然求单源最短路可以迪杰斯特拉,但是他的前提是每一次第一出队的一定是最短的,而显然这个条件在这里不成立(因为后面的可能比当前出对的更小)。

    那么SPFA?它的本质是动态规划,每一次在前一次的基础上松弛,保证了走到某一点在i步范围内是正确的,进而不断转移到n步,(这在没有负环的情况下显然是包含所有情况)而在本题中不断走一个环没有意义,也就是不存在负环。因此SPFA是适用的。

    AC代码:

    1. #include
    2. using namespace std;
    3. int n,m;
    4. int a[100010];
    5. vector<int> edge[500010];
    6. vector<int> edeg[500010];
    7. int maxx[100010],minn[100010];
    8. int st[100010];
    9. void spfa(int type)
    10. {
    11. queue<int> q;
    12. memset(st, 0, sizeof st);
    13. if(type==0){
    14. memset(minn, 0x3f, sizeof(minn));
    15. minn[1] = a[1];
    16. q.push(1);
    17. st[1] = true;
    18. }
    19. else{
    20. memset(maxx, 0xc0, sizeof(maxx));
    21. maxx[n]=a[n];
    22. q.push(n);
    23. st[n]=1;
    24. }
    25. while (!q.empty())
    26. {
    27. int t = q.front();
    28. st[t] = false;
    29. q.pop();
    30. if(type==0){
    31. for (int i =0; i size(); i ++)
    32. {
    33. int j = edge[t][i];
    34. if (minn[j] > min(minn[t],a[j]))
    35. {
    36. minn[j] = min(minn[t],a[j]);
    37. if (!st[j]) // 如果队列中已存在j,则不需要将j重复插入
    38. {
    39. q.push(j);
    40. st[j] = true;
    41. }
    42. }
    43. }}
    44. else{
    45. for (int i =0; i size(); i ++){
    46. int j=edeg[t][i];
    47. if(maxx[j]<max(maxx[t],a[j])){
    48. maxx[j]=max(maxx[t],a[j]);
    49. if (!st[j]) // 如果队列中已存在j,则不需要将j重复插入
    50. {
    51. q.push(j);
    52. st[j] = true;
    53. }
    54. }
    55. }
    56. }
    57. }
    58. }
    59. int main(){
    60. cin>>n>>m;
    61. for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    62. for(int i=1;i<=m;i++){
    63. int x,y,z;
    64. cin>>x>>y>>z;
    65. edge[x].push_back(y);
    66. edeg[y].push_back(x);
    67. if(z==2){
    68. edge[y].push_back(x);
    69. edeg[x].push_back(y);
    70. }
    71. }
    72. spfa(0);
    73. spfa(1);
    74. int res=0;
    75. for(int i=1;i<=n;i++) res=max(res,maxx[i]-minn[i]);
    76. cout<
    77. }

  • 相关阅读:
    【网络安全】什么样的人适合学?该怎么学?
    【数据结构】测试3 栈和队列
    【JavaEE】锁策略
    Leetcode—3148. 矩阵中的最大得分【中等】
    算法练习16——O(1) 时间插入、删除和获取随机元素
    房产新闻查询易语言代码
    spring5.0 源码解析(day05) initMessageSource();
    K-verse 小型活动来袭!
    SpringBoot SpringBoot 开发实用篇 4 数据层解决方案 4.14 ES 索引操作
    配运基础数据缓存瘦身实践
  • 原文地址:https://blog.csdn.net/cocoack/article/details/140994014