题目链接:
https://www.lydsy.com/JudgeOnline/problem.php?id=1061
题目大意:
申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。
思路:
还有一篇讲的比较通俗易懂,用填坑来建图:https://blog.csdn.net/u013368721/article/details/39900027
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 ", cost); 115 return 0; 116 }