[NOI2008] 志愿者招募 洛谷P3980
这是一个看上去很贪心的问题,但是很显然不能够直接贪心。
我们面对这一类问题一般采用 \(dp\) 或者网络流来进行分析。
这里限制很多,状态复杂,因此不适合 dp ,我们不妨采用网络流来描述这样一个问题。
我们发现,每一天都需要至少 \(a_i\) 个志愿者,而每种志愿者都恰好会在一段连续的日子进行工作,我们不妨将每一天拆成两个点用上下界流量来限制每一天的最少志愿者数量。
同时,我们的一种志愿者恰好会在一段区间内工作,我们不妨使用差分的思想进行限制,让这种志愿者从 \(s\) 进入,\(t\) 回来,满足流量守恒,同时在进入的时候需要计算费用。
最后我们只需要跑一遍无源汇最小费用可行流即可。
代码:
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template <typename T>
void read(T &x) {
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s^'0');s=getchar();}
x *= f;
}
const int MAXN = 1e6 + 5;
const int MAXM = 1e6 + 5;
const int inf = 1e9;
int head[MAXN] , to[MAXM << 1] , nxt[MAXM << 1] , cnt = 1;
int edge[MAXM << 1] , val[MAXM << 1];
void add(int u , int v , int c , int w) {
nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v;edge[cnt] = c;val[cnt] = w;
nxt[++cnt] = head[v];head[v] = cnt;to[cnt] = u;edge[cnt] = 0;val[cnt] = -w;
}
int s , t , num , vis[MAXN] , cur[MAXN];
int dis[MAXN];
int MinCost , MaxFlow;
bool bfs() {
for (int i = 1; i <= num; ++i) vis[i] = 0 , dis[i] = inf , cur[i] = head[i];
dis[s] = 0;
queue <int> q;q.push(s);
int flag = 0;
while(!q.empty()) {
int x = q.front();
q.pop();
vis[x] = 0;
for (int i = head[x]; i; i = nxt[i]) {
if(!edge[i]) continue;
int v = to[i];
if(dis[v] > dis[x] + val[i]) {
dis[v] = dis[x] + val[i];
if(v == t) {
flag = 1;
continue;
}
if(!vis[v]) q.push(v) , vis[v] = 1;
}
}
}
return flag;
}
int Dinic(int x , int flow) {
if(x == t) {
MaxFlow += flow;
MinCost += flow * dis[t];
return flow;
}
vis[x] = 1;
int rest = flow;
for (int i = cur[x]; i && rest; i = nxt[i]) {
cur[x] = i;
if(!edge[i]) continue;
int v = to[i];
if(!vis[v] && dis[v] == dis[x] + val[i]) {
int k = Dinic(v , min(rest , edge[i]));
if(!k) dis[v] = -inf;
rest -= k;
edge[i] -= k;
edge[i ^ 1] += k;
}
}
return flow - rest;
}
void MVMC() {
MinCost = MaxFlow = 0;
while(bfs()) Dinic(s , inf);
}
int n , m;
int main() {
read(n),read(m);
num = n * 2 + m + 2;
s = n * 2 + m + 1 , t = n * 2 + m + 2;
for (int i = 1; i <= n; ++i) {
int x;
read(x);
add(i , t , x , 0);
add(s , i + n , x , 0);
add(i , i + n , inf - x , 0);
if(i != n) add(i + n , i + 1 , inf , 0);
}
for (int i = 1; i <= m; ++i) {
int st , ti , c;
read(st),read(ti),read(c);
add(n * 2 + i , st , inf , c);
add(ti + n , n * 2 + i , inf , 0);
}
MVMC();
printf("%d\n" , MinCost);
return 0;
}