Lunch Time

淩亂°似流年 2021-11-10 12:48 309阅读 0赞

hdu4807:http://acm.hdu.edu.cn/showproblem.php?pid=4807

题意:给你n个点(0—n-1),点之间是有向边,0号点有k个人,现在0号点的k个人要到n-1号点。每条边有一个容量,就是单位时间内最多允许c个人通过,通过一条边需要一个单位时间,现在问你最后一个到达n-1号点最短的时间是多少。

题解:这题用到网络流。怎么用呢?首先建图,边的容量自然是原来的容量,费用肯定为1.接着,我们可以想。先选一条路径的话,我们肯定选择一条费用最少的,这里就是距离最短的一条,因为,如果选择其他的一条,当那一条到达时,这一条已经到达,不会影响最后一个人的到达,只会贡献人数的减少。所以肯定选择费用最少的一条。然后,直选一条行不?答案是不一定的,因为这一条仅仅是费用最少,但是容量不一定是最大的。当第一个人到达的时候,接下来每一秒都会有y个人到达,y是流量。所以我们遍历下一条,选择下一条的时候,前一条肯要选,因为前一条不会影响这一条,并且会减少人数,提高运输量。所以,我们要遍历所有可行路径。s[i]表示到第i条时候第一次可以运输的总人数,则可以退出递推关系系s[i]=y[i] +(d[i]-d[i-1])*sum[i-1],y[i]表示第i条可行路径的流量,sum[i-1]表示前i-1条额流量和,d[i]表示第i条路径的费用。然后求费用流的时候更新就可以了。但是还要注意k==0时候要特判。

ContractedBlock.gif ExpandedBlockStart.gif

  1. 1 #include<iostream>
  2. 2 #include<cstdio>
  3. 3 #include<cstring>
  4. 4 #include<algorithm>
  5. 5 #include<queue>
  6. 6 #include<cmath>
  7. 7 #define inf 100000000
  8. 8 using namespace std;
  9. 9 const int E=1000000;
  10. 10 const int N=10002;
  11. 11 struct Node{
  12. 12 int v, cap, cost, next; // re记录逆边的下标。
  13. 13 }edge[E];
  14. 14 int n, m;
  15. 15 long long ans,people;
  16. 16 int k, head[N];
  17. 17 int que[N], pre[N], dis[N];
  18. 18 bool vis[N];
  19. 19 void init(){
  20. //初始化
  21. 20 k=ans=0;
  22. 21 memset(head,-1,sizeof(head));
  23. 22 }
  24. 23 void addEdge(int u, int v, int ca, int co){
  25. 24 edge[k].v = v;
  26. 25 edge[k].cap = ca;
  27. 26 edge[k].cost = co;
  28. 27 edge[k].next = head[u];
  29. 28 head[u] = k ++;
  30. 29 edge[k].v = u;
  31. 30 edge[k].cap = 0;
  32. 31 edge[k].cost = -co;
  33. 32 edge[k].next = head[v];
  34. 33 head[v] = k ++;
  35. 34 }
  36. 35 bool spfa(){ // 源点为0,汇点为n。
  37. 36 int i;
  38. 37 for(i = 1; i <= n; i ++){
  39. 38 dis[i] = inf;
  40. 39 vis[i] = false;
  41. 40 }
  42. 41 queue<int>Q;
  43. 42 Q.push(1);
  44. 43 dis[1]=0;
  45. 44 vis[1] = true;
  46. 45 while(!Q.empty()){ // 这里最好用队列,有广搜的意思,堆栈像深搜。
  47. 46 int u = Q.front();
  48. 47 Q.pop();
  49. 48 for(i = head[u]; i != -1; i = edge[i].next){
  50. 49 int v = edge[i].v;
  51. 50 if(edge[i].cap && dis[v] > dis[u] + edge[i].cost){
  52. 51 dis[v] = dis[u] + edge[i].cost;
  53. 52 pre[v] = i;
  54. 53 if(!vis[v]){
  55. 54 vis[v] = true;
  56. 55 Q.push(v);
  57. 56 }
  58. 57 }
  59. 58 }
  60. 59 vis[u] = false;
  61. 60 }
  62. 61 if(dis[n] == inf) return false;
  63. 62 return true;
  64. 63 }
  65. 64 int end(){
  66. 65 int u, p, sum = inf;
  67. 66 for(u = n; u != 1; u = edge[p^1].v){
  68. //0是超级源点
  69. 67 p = pre[u];
  70. 68 sum = min(sum, edge[p].cap);
  71. 69 }
  72. 70 for(u = n; u != 1; u = edge[p^1].v){
  73. 71 p = pre[u];
  74. 72 edge[p].cap -= sum;
  75. 73 edge[p^1].cap += sum;
  76. 74 }
  77. 75 return sum;
  78. 76 }
  79. 77 int main(){
  80. 78 int t1,t2,t3;
  81. 79 while(~scanf("%d%d%d",&n,&m,&people)&&n>0){
  82. 80 init();//初始化
  83. 81 for(int i=1;i<=m;i++){
  84. 82 scanf("%d%d%d",&t1,&t2,&t3);
  85. 83 t1++;t2++;
  86. 84 addEdge(t1,t2,t3,1);//无向图要建边两次
  87. 85 }
  88. 86 long long s=people,now=0,sum=0;
  89. 87 ans=inf;
  90. 88 while(spfa()){
  91. 89 int y=end();
  92. 90 s-=((long long)dis[n]-now)*sum+y;
  93. 91 if(s<0)s=0;
  94. 92 sum+=y;now=dis[n];
  95. 93 long long temp=(long long)now+(long long)ceil(s*1.0/sum);
  96. 94 if(temp<ans)ans=temp;
  97. 95 }
  98. 96 if(people==0)ans=0;
  99. 97 if(ans==inf)printf("No solution\n");
  100. 98 else
  101. 99 printf("%I64d\n",ans);
  102. 100 }
  103. 101 }

转载于:https://www.cnblogs.com/chujian123/p/3921563.html

发表评论

表情:
评论列表 (有 0 条评论,309人围观)

还没有评论,来说两句吧...

相关阅读