# BZOJ 1061 志愿者招募 最小费用流&&线性规划建模

https://www.lydsy.com/JudgeOnline/problem.php?id=1061

  1 #include<bits/stdc++.h>
2 #define IOS ios::sync_with_stdio(false);//不可再使用scanf printf
3 #define Max(a, b) ((a) > (b) ? (a) : (b))//禁用于函数，会超时
4 #define Min(a, b) ((a) < (b) ? (a) : (b))
5 #define Mem(a) memset(a, 0, sizeof(a))
6 #define Dis(x, y, x1, y1) ((x - x1) * (x - x1) + (y - y1) * (y - y1))
7 #define MID(l, r) ((l) + ((r) - (l)) / 2)
8 #define lson ((o)<<1)
9 #define rson ((o)<<1|1)
10 #pragma comment(linker, "/STACK:102400000,102400000")//栈外挂
11 using namespace std;
12 inline int read()
13 {
14     int x=0,f=1;char ch=getchar();
15     while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
16     while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
17     return x*f;
18 }
19 typedef long long ll;
20 const int maxn = 1000 + 10;
21 const int mod = 100003;//const引用更快，宏定义也更快
22 const int INF = 1e9;
23
24 struct edge
25 {
26     int u, v, c, f, cost;
27     edge(int u, int v, int c, int f, int cost):u(u), v(v), c(c), f(f), cost(cost){}
28 };
29 vector<edge>e;
30 vector<int>G[maxn];
31 int a[maxn];//找增广路每个点的水流量
32 int p[maxn];//每次找增广路反向记录路径
33 int d[maxn];//SPFA算法的最短路
34 int inq[maxn];//SPFA算法是否在队列中
35 int n, m;
36 void init(int n)
37 {
38     for(int i = 0; i <= n; i++)G[i].clear();
39     e.clear();
40 }
41 void addedge(int u, int v, int c, int cost)
42 {
43     e.push_back(edge(u, v, c, 0, cost));
44     e.push_back(edge(v, u, 0, 0, -cost));
45     int m = e.size();
46     G[u].push_back(m - 2);
47     G[v].push_back(m - 1);
48 }
49 bool bellman(int s, int t, int& flow, long long & cost)
50 {
51     for(int i = 0; i <= n + 1; i++)d[i] = INF;//Bellman算法的初始化
52     memset(inq, 0, sizeof(inq));
53     d[s] = 0;inq[s] = 1;//源点s的距离设为0，标记入队
54     p[s] = 0;a[s] = INF;//源点流量为INF（和之前的最大流算法是一样的）
55
56     queue<int>q;//Bellman算法和增广路算法同步进行，沿着最短路拓展增广路，得出的解一定是最小费用最大流
57     q.push(s);
58     while(!q.empty())
59     {
60         int u = q.front();
61         q.pop();
62         inq[u] = 0;//入队列标记删除
63         for(int i = 0; i < G[u].size(); i++)
64         {
65             edge & now = e[G[u][i]];
66             int v = now.v;
67             if(now.c > now.f && d[v] > d[u] + now.cost)
68                 //now.c > now.f表示这条路还未流满（和最大流一样）
69                 //d[v] > d[u] + e.cost Bellman 算法中边的松弛
70             {
71                 d[v] = d[u] + now.cost;//Bellman 算法边的松弛
72                 p[v] = G[u][i];//反向记录边的编号
73                 a[v] = min(a[u], now.c - now.f);//到达v点的水量取决于边剩余的容量和u点的水量
74                 if(!inq[v]){q.push(v);inq[v] = 1;}//Bellman 算法入队
75             }
76         }
77     }
78     if(d[t] == INF)return false;//找不到增广路
79     flow += a[t];//最大流的值，此函数引用flow这个值，最后可以直接求出flow
80     cost += (long long)d[t] * (long long)a[t];//距离乘上到达汇点的流量就是费用
81     for(int u = t; u != s; u = e[p[u]].u)//逆向存边
82     {
83         e[p[u]].f += a[t];//正向边加上流量
84         e[p[u] ^ 1].f -= a[t];//反向边减去流量 （和增广路算法一样）
85     }
86     return true;
87 }
88 int MincostMaxflow(int s, int t, long long & cost)
89 {
90     cost = 0;
91     int flow = 0;
92     while(bellman(s, t, flow, cost));//由于Bellman函数用的是引用，所以只要一直调用就可以求出flow和cost
93     return flow;//返回最大流，cost引用可以直接返回最小费用
94 }
95 int main()
96 {
97     int U = 1e5;
98     scanf("%d%d", &n, &m);
99     int s = 0, t = n + 1, x;
100     addedge(s, 1, U, 0);//从源点到出发点连边 流量为U
101     for(int i = 1; i <= n; i++)
102     {
103         scanf("%d", &x);
104         addedge(i, i + 1, U - x, 0);//把需要的人当做坑来填满 这样流到汇点t的流量会小于U
105     }
106     int u, v, w;
107     for(int i = 1; i <= m; i++)//对于每种类型的员工，连边u->v+1 这样可以保证从u出发到v+1的流量增加 也就是填坑
108     {
109         scanf("%d%d%d", &u, &v, &w);
110         addedge(u, v + 1, INF, w);//流量为INF 费用为w
111     }
112     long long cost;
113     MincostMaxflow(s, t, cost);
114     printf("%lld\n", cost);
115     return 0;
116 }

posted @ 2018-09-11 19:48  _努力努力再努力x  阅读(265)  评论(0编辑  收藏  举报