1061: [Noi2008]志愿者招募
Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 3721 Solved: 2281
[ Submit][ Status][ Discuss]
Description
Input
Output
仅包含一个整数,表示你所设计的最优方案的总费用。
Sample Input
2 3 4
1 2 2
2 3 5
3 3 2
Sample Output
HINT
1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1。
Source
一开始就觉得这题和网络流有关,,但是死也不会建图
(学长建议我,,直接百度题解,,,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<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstring>
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 <int> Q;
vector <int> 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;
}