[codevs 1237] 餐巾计划问题

http://codevs.cn/problem/1237/


题解:

引用《24题》:

把每天分为二分图两个集合中的顶点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;
}


posted @ 2015-02-03 17:35  wfwbz  阅读(166)  评论(0编辑  收藏  举报