1061: [Noi2008]志愿者招募

1061: [Noi2008]志愿者招募

Time Limit: 20 Sec   Memory Limit: 162 MB
Submit: 3721   Solved: 2281
[ Submit][ Status][ Discuss]

Description

  申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难
题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要
Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用
是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这
并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。

Input

  第一行包含两个整数N, M,表示完成项目的天数和可以招募的志愿者的种类。 接下来的一行中包含N 个非负
整数,表示每天至少需要的志愿者人数。 接下来的M 行中每行包含三个整数Si, Ti, Ci,含义如上文所述。为了
方便起见,我们可以认为每类志愿者的数量都是无限多的。

Output

  仅包含一个整数,表示你所设计的最优方案的总费用。

Sample Input

3 3
2 3 4
1 2 2
2 3 5
3 3 2

Sample Output

14

HINT

1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1。

Source

[ Submit][ Status][ Discuss]



一开始就觉得这题和网络流有关,,但是死也不会建图

(学长建议我,,直接百度题解,,,GG)

设Xi:选择Xi名第i类志愿者

Di:第i天至少需要Di名志愿者

那么符合原问题的一组解满足不等式组

∑Xi*S(i,j) >= Dj (j∈[1,n])

S(i,j) = 第i名志愿者能在第j天服务?1:0

然后Ans = ∑Xi*Ci

题目的目标是最小化Ans


考虑将原不等式变形

对于第j个不等式,加上Yj,使得

Yj + ∑Xi*S(i,j) = Dj (j∈[1,n])

这样我们就得到了j个等式

再增添两个Y0 = 0   Yn+1 = 0

对于这n + 2个等式左右各做一个差分,,,(就是用第i个减去第i-1个)

这样我们得到n + 1个等式

记第i个等式的右边为ΔDi

显然∑ΔDi = 0

对于第i个等式,

如果ΔDi > 0 从源点向它连一条容量为ΔDi的边

否则从它向汇点连一条容量为ΔDi的边

我们的目标就是在这张图跑出一个最大流

左右都做完差分后,会发现,对于任意的Xi,Yi,在这n个等式里面都是只出现两次且一次为+一次为-

然后,,根据我们的定义,所有的Xi非负,所有的Yi非正

对于第i个等式,若Xj在这个等式里面带着'+',那么从这个等式向j号志愿者连容量为INF,费用为wj的边,代表这个志愿者能够通过花费wj为这个等式贡献1流量

若Xj在这个等式里面带着'-',那么从Xj这个志愿者向这个等式连容量为INF,费用为0的边,代表每次招募j号志愿者,这个等式的流量就要减少1


类似处理Yi,跑个最小费用最大流即可


貌似X,Y作为中间节点直接舍去就好啦???我TM有病啊。。。。

#include
#include
#include
#include
#include
#include
using namespace std;

const int maxn = 2E4 + 20;
const int maxm = 1E6 + 20;
typedef long long LL;
const LL INF = 1E15;

struct E{
	LL to,cap,flow,w;
	E(){}
	E(LL to,LL cap,LL flow,LL w): to(to),cap(cap),flow(flow),w(w){}
}edgs[maxm];

int n,m,s,t,cnt,tot,Num[maxn],X[maxn],Y[maxn],A[maxn],from[maxn];
LL Ans,cost[maxn],flow[maxn];
bool vis[maxn];

queue  Q;
vector  v[maxn];

bool SPFA()
{
	memset(vis,0,sizeof(vis));
	for (int i = s; i <= t; i++) cost[i] = INF;
	flow[s] = INF;
	cost[s] = 0; Q.push(s); vis[s] = 1;
	while (!Q.empty()) {
		int k = Q.front(); Q.pop();
		vis[k] = 0;
		for (int i = 0; i < v[k].size(); i++) {
			E e = edgs[v[k][i]];
			if (e.cap == e.flow) continue;
			if (e.w + cost[k] >= cost[e.to]) continue;	
			cost[e.to] = e.w + cost[k];
			from[e.to] = v[k][i];
			flow[e.to] = min(flow[k],e.cap - e.flow);
			if (!vis[e.to]) {
				vis[e.to] = 1;
				Q.push(e.to);
			}
		}
	}
	return cost[t] != INF;
}

void Add(int x,int y,LL cap,LL w)
{
	v[x].push_back(cnt);
	edgs[cnt++] = E(y,cap,0,w);
	v[y].push_back(cnt);
	edgs[cnt++] = E(x,0,0,-w);
}

int getint()
{
	char ch = getchar();
	int ret = 0;
	while (ch < '0' || '9' < ch) ch = getchar();
	while ('0' <= ch && ch <= '9')
	ret = ret*10 + ch - '0',ch = getchar();
	return ret;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif

	n = getint(); m = getint();
	for (int i = 1; i <= n; i++) {
		Num[i] = ++tot;
		Y[i] = ++tot;
	}
	Num[n+1] = ++tot;
	for (int i = 1; i <= m; i++) X[i] = ++tot;
	t = ++tot;
	for (int i = 1; i <= n; i++) {
		A[i] = getint();
		Add(Num[i+1],Y[i],INF,0);
		Add(Y[i],Num[i],INF,0);
	}
	for (int i = 1; i <= n + 1; i++) {
		int tmp = A[i] - A[i-1];
		if (tmp >= 0) Add(s,Num[i],tmp,0);
		else Add(Num[i],t,-tmp,0);
	}
	for (int i = 1; i <= m; i++) {
		int x,y,w;
		x = getint();
		y = getint();
		w = getint();
		Add(Num[x],X[i],INF,w);
		Add(X[i],Num[y+1],INF,0);
	}

	while (SPFA()) {
		Ans += cost[t]*flow[t];
		for (int now = t; now != s; now = edgs[from[now]^1].to) {
			edgs[from[now]].flow += flow[t];
			edgs[from[now]^1].flow -= flow[t];
		}
	}
	cout << Ans;
	return 0;
}

你可能感兴趣的:(网络流,线性规划)