[关闭]
@darkproject 2018-02-15T23:57:00.000000Z 字数 3374 阅读 821

最短路及其应用

2018寒假集训


E - Going in Cycle!! (UVA - 11090)

n点m边带权有向图,输出其边权和的平均值最小的回路。假设存在一个平均值为x,则这个回路上的k边之和小于kx。可以转化为k边之和减去kx小于0,即求是否存在边权为原边权减去x的负权回路,spfa判一下二分求最小即可。ps:注意高精度二分的格式

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <queue>
  4. #include <map>
  5. #include <cstring>
  6. #include <vector>
  7. #include <algorithm>
  8. using namespace std;
  9. #define eps 1e-4
  10. const int maxn =55;
  11. int n,m,t;
  12. vector<pair<int,double> >E[maxn];
  13. queue<int>mxr;
  14. int vis[maxn],inq[maxn],viss[maxn];
  15. double d[maxn];
  16. void init()
  17. {
  18. memset(vis,0,sizeof(vis));
  19. memset(inq,0,sizeof(inq));
  20. for(int i=0;i<maxn;i++)
  21. {
  22. E[i].clear();
  23. d[i]=1e7+5;;
  24. }
  25. }
  26. bool spfa(int s)
  27. {
  28. for(int i=0;i<maxn;i++)
  29. d[i]=1e7+5;
  30. memset(inq,0,sizeof(inq));
  31. mxr.push(s);
  32. d[s]=0,inq[s]++;
  33. vis[s]=1;
  34. while(!mxr.empty())
  35. {
  36. int now=mxr.front();
  37. mxr.pop();
  38. vis[now]=0;
  39. viss[now]=1;
  40. for(int i=0;i<E[now].size();i++)
  41. {
  42. int v=E[now][i].first;
  43. if(d[v]>d[now]+E[now][i].second)
  44. {
  45. d[v]=d[now]+E[now][i].second;
  46. if(!vis[v])
  47. {
  48. vis[v]=1;
  49. if(inq[v]>n) return true;
  50. inq[v]++;
  51. mxr.push(v);
  52. }
  53. }
  54. }
  55. }
  56. return false;
  57. }
  58. bool judge(double mid)
  59. {
  60. memset(viss,0,sizeof(viss));
  61. for(int i=1;i<=n;i++)
  62. for(int j=0;j<E[i].size();j++)
  63. E[i][j].second-=mid;
  64. bool flag;
  65. for(int i=1;i<=n;i++)
  66. if(!viss[i]){
  67. flag=spfa(i);
  68. if(flag) break;
  69. }
  70. for(int i=1;i<=n;i++)
  71. for(int j=0;j<E[i].size();j++)
  72. E[i][j].second+=mid;
  73. return flag;
  74. }
  75. int main()
  76. {
  77. int cnt=0;
  78. scanf("%d",&t);
  79. while(t--)
  80. {
  81. scanf("%d%d",&n,&m);
  82. init();
  83. int a,b;
  84. double c,l,r;
  85. l=r=0;
  86. for(int i=0;i<m;i++)
  87. {
  88. scanf("%d%d%lf",&a,&b,&c);
  89. E[a].push_back(make_pair(b,c));
  90. r=max(r,c);
  91. }
  92. int mxr;
  93. printf("Case #%d: ",++cnt);
  94. if(!judge(r+1)) printf("No cycle found.\n");
  95. else
  96. {
  97. while(r-l>eps)
  98. {
  99. double mid=(l+r)/2;
  100. if(judge(mid))
  101. {
  102. r=mid;
  103. }
  104. else
  105. l=mid;
  106. }
  107. printf("%.2f\n",l);
  108. }
  109. }
  110. return 0;
  111. }

F - Halum (UVA - 11478)

带权有向图,我们进行一个操作选择一个结点和一个值d,操作后所有以这个结点为终点的边权值减小d,以其为起点的边权值加上d,让所有边权的最小值>0!!且尽量大。我们设sum(a)为作用于a上所有d之和。二分答案x,我们可以列出一系列不等式组形如:sum(b)-sum(a)w(a,b)-x,利用差分约束进行处理即可。。题目很多细节看代码注释。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cstring>
  5. #include <vector>
  6. #include <map>
  7. #include <queue>
  8. using namespace std;
  9. const int maxn = 505;
  10. const int inf = 0x3f3f3f3f;
  11. int n,m,d[maxn],vis[maxn],inq[maxn];
  12. vector<pair<int,int> >E[maxn];
  13. void init()
  14. {
  15. for(int i=0;i<=n;i++)
  16. {
  17. d[i]=inf;
  18. vis[i]=inq[i]=0;
  19. E[i].clear();
  20. }
  21. }
  22. bool spfa(int s)
  23. {
  24. queue<int>mxr;
  25. d[s]=0,vis[s]=1;
  26. inq[s]++;
  27. mxr.push(s);
  28. while(!mxr.empty())
  29. {
  30. int now=mxr.front();
  31. mxr.pop();
  32. vis[now]=0;
  33. for(int i=0;i<E[now].size();i++)
  34. {
  35. int v=E[now][i].first;
  36. if(d[v]>d[now]+E[now][i].second)
  37. {
  38. d[v]=d[now]+E[now][i].second;
  39. if(!vis[v])
  40. {
  41. vis[v]=1;
  42. if(inq[v]>n+1) return false;
  43. inq[v]++;
  44. mxr.push(v);
  45. }
  46. }
  47. }
  48. }
  49. return true;
  50. }
  51. bool solve(int x)
  52. {
  53. memset(d,inf,sizeof(d));
  54. memset(vis,0,sizeof(vis));
  55. memset(inq,0,sizeof(inq));
  56. for(int i=1;i<=n;i++)
  57. for(int j=0;j<E[i].size();j++)
  58. E[i][j].second-=x;
  59. bool flag=spfa(0);
  60. for(int i=1;i<=n;i++)
  61. for(int j=0;j<E[i].size();j++)
  62. E[i][j].second+=x;
  63. return flag;
  64. }
  65. int main()
  66. {
  67. while(~scanf("%d%d",&n,&m))
  68. {
  69. init();
  70. int u,v,d;
  71. int l,r,ret;
  72. l=1;r=-inf;
  73. for(int i=0;i<m;i++)
  74. {
  75. scanf("%d%d%d",&u,&v,&d);
  76. E[u].push_back(make_pair(v,d));
  77. r=max(r,d);
  78. }
  79. for(int i=1;i<=n;i++)
  80. E[0].push_back(make_pair(i,0));
  81. if(solve(r+1)) printf("Infinite\n");
  82. else if(!solve(1)) printf("No Solution\n");
  83. else{
  84. while(l<=r)
  85. {
  86. int mid=(l+r)>>1;
  87. if(solve(mid))
  88. {
  89. ret=mid;
  90. l=mid+1;
  91. }
  92. else r=mid-1;
  93. }
  94. printf("%d\n",ret);
  95. }
  96. //对特判的分析:首先可以知道存在负环差分约束无解,但是正环可以有解,而图为正环的话,无论如何操作都不可能让最小边>最大边。唯有无环的情况有可能,所以可以直接判断图中有无环来判定inf
  97. //而无环的情况是可以直接让最小边无穷大的(画图分析) 所以总结有三种情况讨论:1.带负环 2.带正环 3.无环,前1个if对应无环的分析,后一个二分和if对存在环的分析 ,而存在环的情况是我们主要要讨论的
  98. //要讨论正环和负环这里直接判负环如果不存在则将答案继续最大化 ,还需要对二分单调性证明, 因为二分答案后问题转化为每条边的权值均不小于x,可以知道如果solve(x)不满足的话solve(x+1)一定不满足
  99. //显然不小于x都不满足怎么可能满足不小于x+1
  100. //ps:蓝书上这里有坑,题意解释为最小值非负,实际题意要求>0
  101. }
  102. return 0;
  103. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注