立志成为饮水机!

洛谷P4015 运输问题 网络流24题

看了下SPFA题解,一个一个太麻烦了,另一个写的很不清楚,而且注释都变成了"????"不知道怎么过的,于是自己来一发SPFA算法。

Part 1.题意

M 个仓库,卖给 N 个商店,两个问,第一问求运价最小值,第二问最大值。

显然是一个最小费用最大流(MCMF)。

Part 2.思路

1.连让每个仓库连接一个超级源点 SS ,费用(dis)为0,流量为仓库的流量,表示每个仓库最多可以运出多少货物。

2.让每一个仓库连接每一家商店,边权为 cost[i][j] ,其中,i为仓库编号,j为商店编号编号,流量为 need[j] ,其实流量可以取得范围是  [need[j]...INF] ,另外如果出现 need[j] <这个仓库货物量的情况也可以不怕(这时候取值的下限变成 min(hw[i],need[j]) ) hw指的是这家仓库的货物,还有注意编号的范围(我默认超级源点是 00 ,仓库是 1……n ,商店是 n+1……n+m ,超级汇点是 10000

3.让每一家商店连接超级汇点 TT

图像帮助理解: 

Part 3.代码

现在代码就好办了 注释给的很清楚

  1 #include<iostream>
  2 #include<cmath>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<queue>
  6 #include<stack>
  7 #include<vector>
  8 #include<map>
  9 #include<set>
 10 #include<algorithm>
 11 
 12 #define I_copy_this_answer return 0;
 13 
 14 using namespace std;
 15 
 16 int n,m,head[1100],size=1;
 17 int mmx=1000,mincost,maxwater;
 18 int flow[1100];
 19 int need[1100],cost[310][310];
 20 int pre[1100],las[1100],dis[1100],vis[1100],hw[1100];
 21 
 22 struct edge{
 23     int next,to,dis,flow; 
 24 }e[100860]; 
 25 
 26 void addedge(int next,int to,int dis,int flow)
 27 {
 28     e[++size].to=to;
 29     e[size].dis=dis;
 30     e[size].flow=flow;
 31     e[size].next=head[next];
 32     head[next]=size;
 33 }
 34 
 35 int spfa(int s)
 36 {
 37     memset(flow,0x3f,sizeof(flow));
 38     memset(dis,0x3f,sizeof(dis));
 39     memset(vis,0,sizeof(vis));
 40     queue <int> q;
 41     q.push(s);
 42     dis[s]=0;
 43     vis[s]=1;
 44     pre[mmx]=-1;  //(其实只要不是与p直接连的点(n+1......n+m)就可以了 
 45     while(!q.empty())
 46     {
 47         int t=q.front();
 48         q.pop();
 49         vis[t]=0;
 50         int i,j,k,l;
 51         for(i=head[t];i;i=e[i].next)
 52         {
 53             j=e[i].to;
 54             k=e[i].dis;
 55             l=e[i].flow;
 56             if(dis[t]+k<dis[j]&&l>0)  //没有流量的话这条路就增广不了,最短距离是建立在增广路存在的基础上的 
 57             {
 58                 dis[j]=dis[t]+k;
 59                 las[j]=i;  //las指的是这个点(j)与上个点(t)相连的边的编号 
 60                 pre[j]=t;  //pre指的是这条路径上这个点(j)的上一个点 
 61                 flow[j]=min(flow[t],l);  //把当前边流量与上个点的流量对比,解决出现仓库货物比需要的少的情况 
 62                 if(!vis[j])
 63                 {
 64                     q.push(j);
 65                     vis[j]=1;
 66                 }
 67             }
 68         }
 69     }
 70     return pre[mmx]!=-1;  //如果不是这个值就说明这个点被刷新,增广成功 
 71 }
 72 
 73 void mcmf()
 74 {
 75     while(spfa(0))
 76     {
 77         mincost+=dis[mmx]*flow[mmx];   //从源点出发到汇点的单位费用再乘以单位,由于每次只增广一条路,而且仓库和商店是直接连接的,可以这样写 
 78         int t=mmx;
 79         while(t!=0)
 80         {
 81             e[las[t]].flow-=flow[mmx];  //回溯,修改每条边的流量,因为该算法中途找到的增广路不是最后的增广路,所以这个要等到最后来改变 
 82             e[las[t]^1].flow+=flow[mmx];
 83             t=pre[t];
 84         }
 85     }
 86 }
 87 
 88 void build_edge(int t)
 89 {
 90     int i,j;
 91     for(i=1;i<=m;i++)
 92     {
 93         addedge(0,i,0,hw[i]);
 94         addedge(i,0,0,0);
 95     } 
 96     for(i=1;i<=m;i++)
 97     for(j=1;j<=n;j++)
 98     {
 99         addedge(i,j+m,cost[i][j]*t,need[j]);
100         addedge(j+m,i,-cost[i][j]*t,0);
101     }
102     for(i=1;i<=n;i++)
103     {
104         addedge(i+m,mmx,0,need[i]);
105         addedge(mmx,i+m,0,0);
106     }
107 }
108 
109 int main()
110 {
111     int i,j;
112     scanf("%d %d",&m,&n);
113     for(i=1;i<=m;i++)
114     {
115         int t1;
116         scanf("%d",&hw[i]); 
117     }
118     for(i=1;i<=n;i++)
119         scanf("%d",&need[i]);
120     for(i=1;i<=m;i++)
121     for(j=1;j<=n;j++)
122         scanf("%d",&cost[i][j]);  //读入,与上面的cost,need,hw如果不明白可以对照输入格式看代表什么意思 
123     build_edge(1);  //建立边权为正的边,跑最小费用最大流 
124     mcmf();//最小费用最大流(Min Cost Max Flow )的缩写 
125     printf("%d",mincost); 
126     maxwater=0;
127     mincost=0; 
128     size=1;
129     memset(head,0,sizeof(head));
130     build_edge(-1);
131     mcmf();
132     printf("\n%d",-mincost);
133     I_copy_this_answer
134 }

 

posted @ 2019-07-12 10:08  寒冰大大  阅读(304)  评论(0编辑  收藏  举报