[codevs 1237] 餐巾计划问题
http://codevs.cn/problem/1237/
题解:
把每天分为二分图两个集合中的顶点Xi,Yi,建立附加源S汇T。
1、从S向每个Xi连一条容量为ri,费用为0的有向边。
2、从每个Yi向T连一条容量为ri,费用为0的有向边。
3、从S向每个Yi连一条容量为无穷大,费用为p的有向边。
4、从每个Xi向Xi+1(i+1<=N)连一条容量为无穷大,费用为0的有向边。
5、从每个Xi向Yi+m(i+m<=N)连一条容量为无穷大,费用为f的有向边。
6、从每个Xi向Yi+n(i+n<=N)连一条容量为无穷大,费用为s的有向边。
求网络最小费用最大流,费用流值就是要求的最小总花费。
【建模分析】
这个问题的主要约束条件是每天的餐巾够用,而餐巾的来源可能是最新购买,也可能是前几天送洗,今天刚刚洗好的餐巾。每天用完的餐巾可以选择送到快洗部或慢洗部,或者留到下一天再处理。
经过分析可以把每天要用的和用完的分离开处理,建模后就是二分图。二分图X集合中顶点Xi表示第i天用完的餐巾,其数量为ri,所以从S向Xi连接容量为ri的边作为限制。Y集合中每个点Yi则是第i天需要的餐巾,数量为ri,与T连接的边容量作为限制。每天用完的餐巾可以选择留到下一天(Xi->Xi+1),不需要花费,送到快洗部(Xi->Yi+m),费用为f,送到慢洗部(Xi->Yi+n),费用为s。每天需要的餐巾除了刚刚洗好的餐巾,还可能是新购买的(S->Yi),费用为p。
在网络上求出的最小费用最大流,满足了问题的约束条件(因为在这个图上最大流一定可以使与T连接的边全部满流,其他边只要有可行流就满足条件),而且还可以保证总费用最小,就是我们的优化目标。
代码:
总时间耗费: 2598ms
总内存耗费: 876B
#include<cstdio> #include<iostream> #include<vector> #include<queue> #include<algorithm> using namespace std; const int maxn = 5000 + 10; const int INF = 1e9 + 7; struct Edge { int from, to, cap, flow, cost; }; int n, s, t; vector<int> G[maxn]; vector<Edge> edges; void AddEdge(int from, int to, int cap, int cost) { edges.push_back((Edge){from, to, cap, 0, cost}); edges.push_back((Edge){to, from, 0, 0, -cost}); int m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } int d[maxn], p[maxn], a[maxn]; bool inq[maxn]; bool BellmanFord(int& cost) { memset(inq, 0, sizeof(inq)); for(int i = s; i <= t; i++) d[i] = INF; d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF; queue<int> Q; Q.push(s); while(!Q.empty()) { int x = Q.front(); Q.pop(); inq[x] = 0; for(int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(e.cap > e.flow && d[e.to] > d[x] + e.cost) { d[e.to] = d[x] + e.cost; a[e.to] = min(a[x], e.cap-e.flow); p[e.to] = G[x][i]; if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; } } } } if(d[t] == INF) return 0; cost += d[t]*a[t]; int x = t; while(x != s) { edges[p[x]].flow += a[t]; edges[p[x]^1].flow -= a[t]; x = edges[p[x]].from; } return 1; } int MincostMaxflow() { int cost = 0; while(BellmanFord(cost)); return cost; } int main() { int n, price, day1, cost1, day2, cost2; cin >> n >> price >> day1 >> cost1 >> day2 >> cost2; s = 0; t = n + n + 1; for(int i = 1; i <= n; i++) { int need; cin >> need; AddEdge(s, i, need, 0); //needed AddEdge(i+n, t, need, 0); //need AddEdge(s, i+n, INF, price); //buy if(i+day1 <= n) AddEdge(i, i+n+day1, INF, cost1); //fast if(i+day2 <= n) AddEdge(i, i+n+day2, INF, cost2); //slow if(i < n) AddEdge(i, i+1, INF, 0); //delay } cout << MincostMaxflow() << endl; return 0; }